From Jerry Talkington:

- Dissect chunked transfer-coded body in HTTP
- Update email address

svn path=/trunk/; revision=10710
This commit is contained in:
Olivier Biot 2004-04-26 17:10:40 +00:00
parent c94e9b9687
commit 9e77ad2862
5 changed files with 419 additions and 44 deletions

View File

@ -285,7 +285,7 @@ Greg Hankins <gregh[AT]twoguys.org> {
updates to BGP (Border Gateway Protocol) support
}
Jerry Talkington <jerryt[AT]netapp.com> {
Jerry Talkington <jtalkington[AT]users.sourceforge.net> {
updates to HTTP support
Filter selection/editing GUI improvements
WCCP 1.0 support

View File

@ -1893,7 +1893,7 @@ B<http://www.ethereal.com>.
Warren Young <tangent[AT]mail.com>
Heikki Vatiainen <hessu[AT]cs.tut.fi>
Greg Hankins <gregh[AT]twoguys.org>
Jerry Talkington <jerryt[AT]netapp.com>
Jerry Talkington <jtalkington[AT]users.sourceforge.net>
Dave Chapeskie <dchapes[AT]ddm.on.ca>
James Coe <jammer[AT]cin.net>
Bert Driehuis <driehuis[AT]playbeing.org>

View File

@ -6,7 +6,7 @@
* Copyright 2002, Tim Potter <tpot@samba.org>
* Copyright 1999, Andrew Tridgell <tridge@samba.org>
*
* $Id: packet-http.c,v 1.96 2004/04/12 22:14:37 guy Exp $
* $Id: packet-http.c,v 1.97 2004/04/26 17:10:40 obiot Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -73,6 +73,9 @@ static int hf_http_transfer_encoding = -1;
static gint ett_http = -1;
static gint ett_http_ntlmssp = -1;
static gint ett_http_request = -1;
static gint ett_http_chunked_response = -1;
static gint ett_http_chunk_data = -1;
static gint ett_http_encoded_entity = -1;
static dissector_handle_t data_handle;
static dissector_handle_t http_handle;
@ -125,6 +128,8 @@ typedef struct {
static int is_http_request_or_reply(const gchar *data, int linelen, http_type_t *type,
RequestDissector *req_dissector, int *req_strlen);
static int chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo,
proto_tree *tree, int offset);
static void process_header(tvbuff_t *tvb, int offset, int next_offset,
const guchar *line, int linelen, int colon_offset, packet_info *pinfo,
proto_tree *tree, headers_t *eh_ptr);
@ -589,6 +594,7 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
*/
tvbuff_t *next_tvb;
void *save_private_data = NULL;
gint chunks_decoded = 0;
/*
* Create a tvbuff for the payload.
@ -607,6 +613,38 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
next_tvb = tvb_new_subset(tvb, offset, datalen,
reported_datalen);
/*
* Handle transfer encodings other than "identity".
*/
if (headers.transfer_encoding != NULL &&
strcasecmp(headers.transfer_encoding, "identity") != 0) {
if (strcasecmp(headers.transfer_encoding, "chunked")
== 0) {
chunks_decoded = chunked_encoding_dissector(
&next_tvb, pinfo, tree, 0);
if (chunks_decoded <= 0) {
/*
* The chunks weren't reassembled,
* or there was a single zero
* length chunk.
*/
goto body_dissected;
}
} else {
/*
* We currently can't handle, for example, "gzip",
* "compress", or "deflate"; just handle them
* as data for now.
*/
call_dissector(data_handle, next_tvb, pinfo,
http_tree);
goto body_dissected;
}
}
/*
* Handle content encodings other than "identity" (which
* shouldn't appear in a Content-Encoding header, but
@ -619,23 +657,28 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
* "compress", or "deflate"; just handle them as
* data for now.
*/
call_dissector(data_handle, next_tvb, pinfo,
http_tree);
goto body_dissected;
}
if (chunks_decoded != 0) {
/*
* There is a chunked response tree, so put
* the entity body below it.
*/
proto_item *e_ti = NULL;
proto_tree *e_tree = NULL;
/*
* Handle transfer encodings other than "identity".
*/
if (headers.transfer_encoding != NULL &&
strcasecmp(headers.transfer_encoding, "identity") != 0) {
/*
* We currently can't handle, for example, "chunked",
* "gzip", "compress", or "deflate"; just handle them
* as data for now.
*/
call_dissector(data_handle, next_tvb, pinfo,
http_tree);
e_ti = proto_tree_add_text(tree, next_tvb,
0, tvb_length(next_tvb),
"Encoded entity-body (%s)",
headers.content_encoding);
e_tree = proto_item_add_subtree(e_ti,
ett_http_encoded_entity);
call_dissector(data_handle, next_tvb, pinfo,
e_tree);
} else {
call_dissector(data_handle, next_tvb, pinfo,
http_tree);
}
goto body_dissected;
}
@ -750,6 +793,186 @@ basic_response_dissector(tvbuff_t *tvb, proto_tree *tree, int req_strlen _U_)
}
}
/*
* Dissect the http data chunks and add them to the tree.
*/
static int
chunked_encoding_dissector(tvbuff_t **tvb_ptr, packet_info *pinfo,
proto_tree *tree, int offset)
{
guint8 *chunk_string = NULL;
gint chunk_size = 0;
gint chunk_offset = 0;
gint datalen = 0;
gint linelen = 0;
gint chunks_decoded = 0;
tvbuff_t *tvb = NULL;
tvbuff_t *new_tvb = NULL;
gint chunked_data_size = 0;
proto_tree *subtree = NULL;
proto_item *ti = NULL;
if (tvb_ptr == NULL || *tvb_ptr == NULL) {
return 0;
}
tvb = *tvb_ptr;
datalen = tvb_reported_length_remaining(tvb, offset);
if (tree) {
ti = proto_tree_add_text(tree, tvb, offset, datalen,
"HTTP chunked response");
subtree = proto_item_add_subtree(ti, ett_http_chunked_response);
}
while (datalen != 0) {
proto_item *chunk_ti = NULL;
proto_tree *chunk_subtree = NULL;
tvbuff_t *data_tvb = NULL;
gchar *c = NULL;
linelen = tvb_find_line_end(tvb, offset, -1, &chunk_offset, TRUE);
if (linelen <= 0) {
/* Can't get the chunk size line */
return chunks_decoded;
}
chunk_string = tvb_get_string(tvb, offset, linelen);
if (chunk_string == NULL) {
/* Can't get the chunk size line */
return chunks_decoded;
}
c = chunk_string;
/*
* We don't care about the extensions.
*/
if ((c = strchr(c, ';'))) {
*c = '\0';
}
if (sscanf(chunk_string, "%x", &chunk_size) != 1) {
g_free(chunk_string);
return chunks_decoded;
}
g_free(chunk_string);
if (chunk_size > datalen) {
/*
* The chunk size is more than what's in the tvbuff,
* so either the user hasn't enabled decoding, or all
* of the segments weren't captured.
*/
chunk_size = datalen;
}/* else if (new_tvb == NULL) {
new_tvb = tvb_new_composite();
}
if (new_tvb != NULL && chunk_size != 0) {
tvbuff_t *chunk_tvb = NULL;
chunk_tvb = tvb_new_subset(tvb, chunk_offset,
chunk_size, datalen);
tvb_composite_append(new_tvb, chunk_tvb);
}
*/
chunked_data_size += chunk_size;
if (chunk_size != 0) {
guint8 *raw_data = g_malloc(chunked_data_size);
gint raw_len = 0;
if (new_tvb != NULL) {
raw_len = tvb_length_remaining(new_tvb, 0);
tvb_memcpy(new_tvb, raw_data, 0, raw_len);
tvb_free(new_tvb);
}
tvb_memcpy(tvb, (guint8 *)(raw_data + raw_len),
chunk_offset, chunk_size);
new_tvb = tvb_new_real_data(raw_data,
chunked_data_size, chunked_data_size);
}
if (subtree) {
if (chunk_size == 0) {
chunk_ti = proto_tree_add_text(subtree, tvb,
offset,
chunk_offset - offset + chunk_size + 2,
"Data chunk (last chunk)");
} else {
chunk_ti = proto_tree_add_text(subtree, tvb,
offset,
chunk_offset - offset + chunk_size + 2,
"Data chunk (%u octets)", chunk_size);
}
chunk_subtree = proto_item_add_subtree(chunk_ti,
ett_http_chunk_data);
proto_tree_add_text(chunk_subtree, tvb, offset,
chunk_offset - offset, "Chunk size: %u octets",
chunk_size);
data_tvb = tvb_new_subset(tvb, chunk_offset, chunk_size,
datalen);
if (chunk_size > 0) {
call_dissector(data_handle, data_tvb, pinfo,
chunk_subtree);
}
proto_tree_add_text(chunk_subtree, tvb, chunk_offset +
chunk_size, 2, "Chunk boundary");
}
chunks_decoded++;
offset = chunk_offset + chunk_size + 2;
datalen = tvb_reported_length_remaining(tvb, offset);
}
if (new_tvb != NULL) {
/* Placeholder for the day that composite tvbuffer's will work.
tvb_composite_finalize(new_tvb);
/ * tvb_set_reported_length(new_tvb, chunked_data_size); * /
*/
tvb_set_child_real_data_tvbuff(tvb, new_tvb);
add_new_data_source(pinfo, new_tvb, "De-chunked entity body");
tvb_free(*tvb_ptr);
*tvb_ptr = new_tvb;
} else {
/*
* We didn't create a new tvb, so don't allow sub dissectors
* try to decode the non-existant entity body.
*/
chunks_decoded = -1;
}
return chunks_decoded;
}
/*
* XXX - this won't handle HTTP 0.9 replies, but they're all data
* anyway.
@ -1271,6 +1494,9 @@ proto_register_http(void)
&ett_http,
&ett_http_ntlmssp,
&ett_http_request,
&ett_http_chunked_response,
&ett_http_chunk_data,
&ett_http_encoded_entity,
};
module_t *http_module;

View File

@ -1,8 +1,8 @@
/* packet-wccp.c
* Routines for Web Cache Coordination Protocol dissection
* Jerry Talkington <jerryt@netapp.com>
* Jerry Talkington <jtalkington@users.sourceforge.net>
*
* $Id: packet-wccp.c,v 1.33 2002/08/28 21:00:37 jmayer Exp $
* $Id: packet-wccp.c,v 1.34 2004/04/26 17:10:40 obiot Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>

View File

@ -2,7 +2,7 @@
* Routines handling protocols with a request/response line, headers,
* a blank line, and an optional body.
*
* $Id: req_resp_hdrs.c,v 1.3 2003/12/29 22:33:18 guy Exp $
* $Id: req_resp_hdrs.c,v 1.4 2004/04/26 17:10:40 obiot Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
@ -30,6 +30,7 @@
#include <glib.h>
#include <epan/packet.h>
#include <epan/strutil.h>
#include <string.h>
#include "req_resp_hdrs.h"
@ -47,6 +48,7 @@ req_resp_hdrs_do_reassembly(tvbuff_t *tvb, packet_info *pinfo,
int linelen;
long int content_length;
gboolean content_length_found = FALSE;
gboolean chunked_encoding = FALSE;
/*
* Do header desegmentation if we've been told to.
@ -131,8 +133,8 @@ req_resp_hdrs_do_reassembly(tvbuff_t *tvb, packet_info *pinfo,
}
/*
* 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
@ -151,6 +153,44 @@ req_resp_hdrs_do_reassembly(tvbuff_t *tvb, packet_info *pinfo,
"%li", &content_length)
== 1)
content_length_found = TRUE;
} else if (tvb_strncaseeql(tvb,
next_offset_sav,
"Transfer-Encoding:", 18) == 0) {
gchar *chunk_type = tvb_get_string(tvb,
next_offset_sav + 18, linelen - 18);
/*
* 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.
*/
if (chunk_type != NULL) {
gchar *c = chunk_type;
gint len = strlen(chunk_type);
/* start after any white-space */
while (c != NULL && c <
chunk_type + len &&
(*c == ' ' ||
*c == 0x09)) {
c++;
}
if (c <= chunk_type + len ) {
if (strncasecmp(c, "chunked", 7)
== 0) {
/*
* Don't bother looking for extensions;
* since we don't understand them,
* they should be ignored.
*/
chunked_encoding = TRUE;
}
}
g_free(chunk_type);
}
}
}
}
@ -158,30 +198,139 @@ req_resp_hdrs_do_reassembly(tvbuff_t *tvb, packet_info *pinfo,
/*
* 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) {
/*
* It's a waste of time asking for more
* data, because that data wasn't captured.
*/
return TRUE;
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;
/*
* We don't care about the extensions.
*/
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;
}
}
/*