2 * Routines for multipart media encapsulation dissection
3 * Copyright 2004, Anders Broman.
4 * Copyright 2004, Olivier Biot.
6 * Refer to the AUTHORS file or the AUTHORS section in the man page
7 * for contacting the author(s) of this file.
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
14 * SPDX-License-Identifier: GPL-2.0-or-later
16 * References for "media-type multipart/mixed :
17 * https://www.iana.org/assignments/media-types/index.html
18 * https://tools.ietf.org/html/rfc2045
19 * https://tools.ietf.org/html/rfc2046
20 * https://tools.ietf.org/html/rfc2047
21 * https://tools.ietf.org/html/rfc2048
22 * https://tools.ietf.org/html/rfc2049
24 * Part of the code is modeled from the SIP and HTTP dissectors
26 * General format of a MIME multipart document:
27 * [ preamble line-end ]
28 * dash-boundary transport-padding line-end
31 * close-delimiter transport-padding
32 * [ line-end epilogue ]
35 * dash-boundary := "--" boundary
36 * encapsulation := delimiter transport-padding line-end body-part
37 * delimiter := line-end body-part
38 * close-delimiter := delimiter "--"
39 * body-part := MIME-part-headers [ line-end *OCTET ]
40 * transport-padding := *LWSP-char
42 * Note that line-end is often a LF instead of a CRLF.
47 #include <epan/packet.h>
48 #include <epan/expert.h>
49 #include <epan/media_params.h>
50 #include <epan/prefs.h>
51 #include <wsutil/str_util.h>
52 #include "packet-imf.h"
54 #include <wsutil/array.h>
56 #include "packet-gssapi.h"
57 #include "packet-media-type.h"
59 void proto_register_multipart(void);
60 void proto_reg_handoff_multipart(void);
62 /* Dissector table for media requiring special attention in multipart
64 static dissector_table_t multipart_media_subdissector_table
;
66 /* Initialize the protocol and registered fields */
67 static int proto_multipart
;
69 /* Generated from convert_proto_tree_add_text.pl */
70 static int hf_multipart_trailer
;
71 static int hf_multipart_boundary
;
72 static int hf_multipart_first_boundary
;
73 static int hf_multipart_last_boundary
;
74 static int hf_multipart_preamble
;
76 /* Initialize the subtree pointers */
77 static int ett_multipart
;
78 static int ett_multipart_main
;
79 static int ett_multipart_body
;
81 /* Generated from convert_proto_tree_add_text.pl */
82 static expert_field ei_multipart_no_required_parameter
;
83 static expert_field ei_multipart_decryption_not_possible
;
85 /* Not sure that compact_name exists for multipart, but choose to keep
86 * the structure from SIP dissector, all the content- is also from SIP */
91 const char *compact_name
;
94 static const multipart_header_t multipart_headers
[] = {
95 { "Unknown-header", NULL
}, /* Pad so that the real headers start at index 1 */
96 { "Content-Description", NULL
},
97 { "Content-Disposition", NULL
},
98 { "Content-Encoding", "e" },
99 { "Content-Id", NULL
},
100 { "Content-Language", NULL
},
101 { "Content-Length", "l" },
102 { "Content-Transfer-Encoding", NULL
},
103 { "Content-Type", "c" },
104 { "OriginalContent", NULL
}
107 #define POS_CONTENT_DESCRIPTION 1
108 #define POS_CONTENT_DISPOSITION 2
109 #define POS_CONTENT_ENCODING 3
110 #define POS_CONTENT_ID 4
111 #define POS_CONTENT_LANGUAGE 5
112 #define POS_CONTENT_LENGTH 6
113 #define POS_CONTENT_TRANSFER_ENCODING 7
114 #define POS_CONTENT_TYPE 8
115 #define POS_ORIGINALCONTENT 9
117 /* Initialize the header fields */
118 static int hf_multipart_type
;
119 static int hf_multipart_part
;
120 static int hf_multipart_sec_token_len
;
121 static int hf_header_array
[array_length(multipart_headers
)];
123 /* Define media_type/Content type table */
124 static dissector_table_t media_type_dissector_table
;
126 /* Data and media dissector handles */
127 static dissector_handle_t multipart_handle
;
128 static dissector_handle_t media_handle
;
129 static dissector_handle_t gssapi_handle
;
131 /* Determines if bodies with no media type dissector should be displayed
132 * as raw text, may cause problems with images sound etc
133 * TODO improve to check for different content types ?
135 static bool display_unknown_body_as_text
;
136 static bool remove_base64_encoding
;
137 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
138 static bool uncompress_data
= true;
142 const char *type
; /* Type of multipart */
143 char *boundary
; /* Boundary string (enclosing quotes removed if any) */
144 unsigned boundary_length
; /* Length of the boundary string */
145 char *protocol
; /* Protocol string if encrypted multipart (enclosing quotes removed if any) */
146 unsigned protocol_length
; /* Length of the protocol string */
147 char *orig_content_type
; /* Content-Type of original message */
148 char *orig_parameters
; /* Parameters for Content-Type of original message */
154 find_first_boundary(tvbuff_t
*tvb
, int start
, const uint8_t *boundary
,
155 int boundary_len
, int *boundary_line_len
, bool *last_boundary
);
157 find_next_boundary(tvbuff_t
*tvb
, int start
, const uint8_t *boundary
,
158 int boundary_len
, int *boundary_line_len
, bool *last_boundary
);
160 process_preamble(proto_tree
*tree
, tvbuff_t
*tvb
, multipart_info_t
*m_info
,
161 bool *last_boundary
);
163 process_body_part(proto_tree
*tree
, tvbuff_t
*tvb
,
164 media_content_info_t
*input_content_info
, multipart_info_t
*m_info
,
165 packet_info
*pinfo
, int start
, int idx
,
166 bool *last_boundary
);
168 is_known_multipart_header(const char *header_str
, unsigned len
);
171 /* Return a tvb that contains the binary representation of a base64
175 base64_decode(packet_info
*pinfo
, tvbuff_t
*b64_tvb
, char *name
)
179 data
= tvb_get_string_enc(pinfo
->pool
, b64_tvb
, 0, tvb_reported_length(b64_tvb
), ENC_ASCII
);
181 tvb
= base64_to_tvb(b64_tvb
, data
);
182 add_new_data_source(pinfo
, tvb
, name
);
188 * Unfold and clean up a MIME-like header, and process LWS as follows:
189 * o Preserves LWS in quoted text
190 * o Remove LWS before and after a separator
191 * o Remove trailing LWS
192 * o Replace other LWS with a single space
193 * Set value to the start of the value
194 * Return the cleaned-up RFC2822 header (buffer must be freed).
197 unfold_and_compact_mime_header(wmem_allocator_t
*pool
, const char *lines
, int *first_colon_offset
)
199 const char *p
= lines
;
202 char sep_seen
= 0; /* Did we see a separator ":;," */
203 char lws
= false; /* Did we see LWS (incl. folding) */
206 if (! lines
) return NULL
;
209 ret
= (char *)wmem_alloc(pool
, strlen(lines
) + 1);
214 lws
= false; /* Prevent leading LWS from showing up */
215 if (colon
== -1) {/* First colon */
216 colon
= (int) (q
- ret
);
218 *(q
++) = sep_seen
= c
;
220 } else if (c
== ';' || c
== ',' || c
== '=') {
221 lws
= false; /* Prevent leading LWS from showing up */
222 *(q
++) = sep_seen
= c
;
224 } else if (c
== ' ' || c
== '\t') {
227 } else if (c
== '\n') {
228 lws
= false; /* Skip trailing LWS */
230 if (c
== ' ' || c
== '\t') { /* Header unfolding */
234 *q
= c
= 0; /* Stop */
237 } else if (c
== '\r') {
242 if (c
== ' ' || c
== '\t') { /* Header unfolding */
246 *q
= c
= 0; /* Stop */
249 } else if (c
== ' ' || c
== '\t') { /* Header unfolding */
253 *q
= c
= 0; /* Stop */
256 } else if (c
== '"') { /* Start of quoted-string */
262 /* First part of a quoted-pair; copy the other part,
263 without checking if it's a quote */
267 p
++; /* Skip closing quote */
272 /* if already zero terminated now, rewind one char to avoid an "off by one" */
276 } else { /* Regular character */
295 *first_colon_offset
= colon
;
299 /* Retrieve the media information from pinfo->private_data,
300 * and compute the boundary string and its length.
301 * Return a pointer to a filled-in multipart_info_t, or NULL on failure.
303 * Boundary delimiters must not appear within the encapsulated material,
304 * and must be no longer than 70 characters, not counting the two
305 * leading hyphens. (quote from rfc2046)
307 static multipart_info_t
*
308 get_multipart_info(packet_info
*pinfo
, media_content_info_t
*content_info
)
310 char *start_boundary
, *start_protocol
= NULL
;
311 multipart_info_t
*m_info
= NULL
;
312 const char *type
= pinfo
->match_string
;
317 * We need both a content type AND parameters
318 * for multipart dissection.
323 if (content_info
== NULL
) {
326 if (content_info
->media_str
== NULL
) {
330 /* Clean up the parameters */
331 parameters
= unfold_and_compact_mime_header(pinfo
->pool
, content_info
->media_str
, &dummy
);
333 start_boundary
= ws_find_media_type_parameter(pinfo
->pool
, parameters
, "boundary");
334 if (!start_boundary
) {
338 if (strncmp(type
, "multipart/encrypted", sizeof("multipart/encrypted") - 1) == 0) {
339 start_protocol
= ws_find_media_type_parameter(pinfo
->pool
, parameters
, "protocol");
340 if (!start_protocol
) {
346 * There is a value for the boundary string
348 m_info
= wmem_new(pinfo
->pool
, multipart_info_t
);
350 m_info
->boundary
= start_boundary
;
351 m_info
->boundary_length
= (unsigned)strlen(start_boundary
);
353 m_info
->protocol
= start_protocol
;
354 m_info
->protocol_length
= (unsigned)strlen(start_protocol
);
356 m_info
->protocol
= NULL
;
357 m_info
->protocol_length
= -1;
359 m_info
->orig_content_type
= NULL
;
360 m_info
->orig_parameters
= NULL
;
366 * The first boundary does not implicitly contain the leading
369 * Return the offset to the 1st byte of the boundary delimiter line.
370 * Set boundary_line_len to the length of the entire boundary delimiter.
371 * Set last_boundary to true if we've seen the last-boundary delimiter.
374 find_first_boundary(tvbuff_t
*tvb
, int start
, const uint8_t *boundary
,
375 int boundary_len
, int *boundary_line_len
, bool *last_boundary
)
377 int offset
= start
, next_offset
, line_len
, boundary_start
;
379 while (tvb_offset_exists(tvb
, offset
+ 2 + boundary_len
)) {
380 boundary_start
= offset
;
381 if (((tvb_strneql(tvb
, offset
, (const uint8_t *)"--", 2) == 0)
382 && (tvb_strneql(tvb
, offset
+ 2, boundary
, boundary_len
) == 0)))
384 /* Boundary string; now check if last */
385 if ((tvb_reported_length_remaining(tvb
, offset
+ 2 + boundary_len
+ 2) >= 0)
386 && (tvb_strneql(tvb
, offset
+ 2 + boundary_len
,
387 (const uint8_t *)"--", 2) == 0)) {
388 *last_boundary
= true;
390 *last_boundary
= false;
392 /* Look for line end of the boundary line */
393 line_len
= tvb_find_line_end(tvb
, offset
, -1, &offset
, false);
394 if (line_len
== -1) {
395 *boundary_line_len
= -1;
397 *boundary_line_len
= offset
- boundary_start
;
399 return boundary_start
;
401 line_len
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
402 if (line_len
== -1) {
405 offset
= next_offset
;
412 * Unless the first boundary, subsequent boundaries include a line-end sequence
413 * before the dashed boundary string.
415 * Return the offset to the 1st byte of the boundary delimiter line.
416 * Set boundary_line_len to the length of the entire boundary delimiter.
417 * Set last_boundary to true if we've seen the last-boundary delimiter.
420 find_next_boundary(tvbuff_t
*tvb
, int start
, const uint8_t *boundary
,
421 int boundary_len
, int *boundary_line_len
, bool *last_boundary
)
423 int offset
= start
, next_offset
, line_len
, boundary_start
;
425 while (tvb_offset_exists(tvb
, offset
+ 2 + boundary_len
)) {
426 line_len
= tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
427 if (line_len
== -1) {
430 boundary_start
= offset
+ line_len
;
431 if (((tvb_strneql(tvb
, next_offset
, (const uint8_t *)"--", 2) == 0)
432 && (tvb_strneql(tvb
, next_offset
+ 2, boundary
, boundary_len
) == 0)))
434 /* Boundary string; now check if last */
435 if ((tvb_reported_length_remaining(tvb
, next_offset
+ 2 + boundary_len
+ 2) >= 0)
436 && (tvb_strneql(tvb
, next_offset
+ 2 + boundary_len
,
437 (const uint8_t *)"--", 2) == 0)) {
438 *last_boundary
= true;
440 *last_boundary
= false;
442 /* Look for line end of the boundary line */
443 line_len
= tvb_find_line_end(tvb
, next_offset
, -1, &offset
, false);
444 if (line_len
== -1) {
445 *boundary_line_len
= -1;
447 *boundary_line_len
= offset
- boundary_start
;
449 return boundary_start
;
450 /* check if last before CRLF; some ignore the standard, so there is no CRLF before the boundary */
451 } else if ((tvb_strneql(tvb
, boundary_start
- 2, (const uint8_t *)"--", 2) == 0)
452 && (tvb_strneql(tvb
, boundary_start
- (2 + boundary_len
), boundary
, boundary_len
) == 0)
453 && (tvb_strneql(tvb
, boundary_start
- (2 + boundary_len
+ 2),
454 (const uint8_t *)"--", 2) == 0)) {
455 boundary_start
-= 2 + boundary_len
+ 2;
456 *boundary_line_len
= next_offset
- boundary_start
;
457 *last_boundary
= true;
458 return boundary_start
;
460 offset
= next_offset
;
467 * Process the multipart preamble:
468 * [ preamble line-end ] dashed-boundary transport-padding line-end
470 * Return the offset to the start of the first body-part.
473 process_preamble(proto_tree
*tree
, tvbuff_t
*tvb
, multipart_info_t
*m_info
,
476 int boundary_start
, boundary_line_len
;
478 const uint8_t *boundary
= (uint8_t *)m_info
->boundary
;
479 int boundary_len
= m_info
->boundary_length
;
481 boundary_start
= find_first_boundary(tvb
, 0, boundary
, boundary_len
,
482 &boundary_line_len
, last_boundary
);
483 if (boundary_start
== 0) {
484 proto_tree_add_item(tree
, hf_multipart_first_boundary
, tvb
, boundary_start
, boundary_line_len
, ENC_NA
|ENC_ASCII
);
485 return boundary_start
+ boundary_line_len
;
486 } else if (boundary_start
> 0) {
487 if (boundary_line_len
> 0) {
488 int body_part_start
= boundary_start
+ boundary_line_len
;
489 proto_tree_add_item(tree
, hf_multipart_preamble
, tvb
, 0, boundary_start
, ENC_NA
);
490 proto_tree_add_item(tree
, hf_multipart_first_boundary
, tvb
, boundary_start
, boundary_line_len
, ENC_NA
|ENC_ASCII
);
491 return body_part_start
;
498 dissect_kerberos_encrypted_message(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, gssapi_encrypt_info_t
* encrypt
)
500 tvbuff_t
*kerberos_tvb
;
504 proto_tree_add_item(tree
, hf_multipart_sec_token_len
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
506 len
= tvb_reported_length_remaining(tvb
, offset
);
508 DISSECTOR_ASSERT(tvb_bytes_exist(tvb
, offset
, len
));
510 data
= (uint8_t *)tvb_memdup(pinfo
->pool
, tvb
, offset
, len
);
511 kerberos_tvb
= tvb_new_child_real_data(tvb
, data
, len
, len
);
513 add_new_data_source(pinfo
, kerberos_tvb
, "Kerberos Data");
514 call_dissector_with_data(gssapi_handle
, kerberos_tvb
, pinfo
, tree
, encrypt
);
518 * Process a multipart body-part:
519 * MIME-part-headers [ line-end *OCTET ]
520 * line-end dashed-boundary transport-padding line-end
522 * If applicable, call a media subdissector.
524 * Return the offset to the start of the next body-part.
527 process_body_part(proto_tree
*tree
, tvbuff_t
*tvb
,
528 media_content_info_t
*input_content_info
, multipart_info_t
*m_info
,
529 packet_info
*pinfo
, int start
, int idx
,
534 int offset
= start
, next_offset
= 0;
535 media_content_info_t content_info
= { input_content_info
->type
, NULL
, NULL
, NULL
};
536 int body_start
, boundary_start
, boundary_line_len
;
538 char *content_type_str
= NULL
;
539 char *content_trans_encoding_str
= NULL
;
540 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
541 char *content_encoding_str
= NULL
;
543 char *filename
= NULL
;
544 char *mimetypename
= NULL
;
545 bool last_field
= false;
546 bool is_raw_data
= false;
548 const uint8_t *boundary
= (uint8_t *)m_info
->boundary
;
549 int boundary_len
= m_info
->boundary_length
;
551 ti
= proto_tree_add_item(tree
, hf_multipart_part
, tvb
, start
, 0, ENC_ASCII
);
552 subtree
= proto_item_add_subtree(ti
, ett_multipart_body
);
554 /* find the next boundary to find the end of this body part */
555 boundary_start
= find_next_boundary(tvb
, offset
, boundary
, boundary_len
,
556 &boundary_line_len
, last_boundary
);
558 if (boundary_start
<= 0) {
563 * Process the MIME-part-headers
572 /* Look for the end of the header (denoted by cr)
573 * 3:d argument to imf_find_field_end() maxlen; must be last offset in the tvb.
575 next_offset
= imf_find_field_end(tvb
, offset
, tvb_reported_length_remaining(tvb
, offset
)+offset
, &last_field
);
576 /* the following should never happen */
577 /* If cr not found, won't have advanced - get out to avoid infinite loop! */
579 if (next_offset == offset) {
583 if (last_field
&& (next_offset
+2) <= boundary_start
) {
584 /* Add the extra CRLF of the last field */
586 } else if((next_offset
-2) == boundary_start
) {
587 /* if CRLF is the start of next boundary it belongs to the boundary and not the field,
588 so it's the last field without CRLF */
591 } else if (next_offset
> boundary_start
) {
592 /* if there is no CRLF between last field and next boundary - trim it! */
593 next_offset
= boundary_start
;
596 hdr_str
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, next_offset
- offset
, ENC_ASCII
);
599 header_str
= unfold_and_compact_mime_header(pinfo
->pool
, hdr_str
, &colon_offset
);
600 if (colon_offset
<= 0) {
601 /* if there is no colon it's no header, so break and add complete line to the body */
602 next_offset
= offset
;
607 hf_index
= is_known_multipart_header(header_str
, colon_offset
);
609 if (hf_index
== -1) {
610 if(isprint_string(header_str
)) {
611 proto_tree_add_format_text(subtree
, tvb
, offset
, next_offset
- offset
);
613 /* if the header name is unknown and not printable, break and add complete line to the body */
614 next_offset
= offset
;
618 char *value_str
= wmem_strdup(pinfo
->pool
, header_str
+ colon_offset
+ 1);
620 proto_tree_add_string_format(subtree
,
621 hf_header_array
[hf_index
], tvb
,
622 offset
, next_offset
- offset
,
623 (const char *)value_str
, "%s",
624 tvb_format_text(pinfo
->pool
, tvb
, offset
, next_offset
- offset
));
627 case POS_ORIGINALCONTENT
:
630 /* The Content-Type starts at colon_offset + 1 or after the type parameter */
631 char* type_str
= ws_find_media_type_parameter(pinfo
->pool
, value_str
, "type");
632 if(type_str
!= NULL
) {
633 value_str
= type_str
;
636 semicolonp
= strchr(value_str
, ';');
638 if (semicolonp
!= NULL
) {
640 m_info
->orig_parameters
= wmem_strdup(pinfo
->pool
,
644 m_info
->orig_content_type
= wmem_ascii_strdown(pinfo
->pool
, value_str
, -1);
647 case POS_CONTENT_TYPE
:
649 /* The Content-Type starts at colon_offset + 1 */
650 char *semicolonp
= strchr(value_str
, ';');
652 if (semicolonp
!= NULL
) {
654 content_info
.media_str
= wmem_strdup(pinfo
->pool
, semicolonp
+ 1);
656 content_info
.media_str
= NULL
;
659 content_type_str
= wmem_ascii_strdown(pinfo
->pool
, value_str
, -1);
661 /* Show content-type in root 'part' label */
662 proto_item_append_text(ti
, " (%s)", content_type_str
);
664 /* find the "name" parameter in case we don't find a content disposition "filename" */
665 mimetypename
= ws_find_media_type_parameter(pinfo
->pool
, content_info
.media_str
, "name");
667 if(strncmp(content_type_str
, "application/octet-stream",
668 sizeof("application/octet-stream")-1) == 0) {
672 /* there are only 2 body parts possible and each part has specific content types */
673 if(m_info
->protocol
&& idx
== 0
674 && (is_raw_data
|| g_ascii_strncasecmp(content_type_str
, m_info
->protocol
,
675 strlen(m_info
->protocol
)) != 0))
681 case POS_CONTENT_ENCODING
:
683 /* The Content-Encoding starts at colon_offset + 1 */
684 char *crp
= strchr(value_str
, '\r');
689 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
690 content_encoding_str
= wmem_ascii_strdown(pinfo
->pool
, value_str
, -1);
694 case POS_CONTENT_TRANSFER_ENCODING
:
696 /* The Content-Transferring starts at colon_offset + 1 */
697 char *crp
= strchr(value_str
, '\r');
703 content_trans_encoding_str
= wmem_ascii_strdown(pinfo
->pool
, value_str
, -1);
706 case POS_CONTENT_DISPOSITION
:
708 /* find the "filename" parameter */
709 filename
= ws_find_media_type_parameter(pinfo
->pool
, value_str
, "filename");
713 content_info
.content_id
= wmem_strdup(pinfo
->pool
, value_str
);
720 offset
= next_offset
;
723 body_start
= next_offset
;
730 int body_len
= boundary_start
- body_start
;
731 tvbuff_t
*tmp_tvb
= tvb_new_subset_length(tvb
, body_start
, body_len
);
733 * If multipart subtype is encrypted the protcol string was set.
735 * See MS-WSMV section 2.2.9.1.2.1 "HTTP Headers":
737 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wsmv/b79927c2-96be-4801-aa68-180db95593f9
739 * There are only 2 body parts possible, and each part has specific
742 if(m_info
->protocol
&& idx
== 1 && is_raw_data
)
744 gssapi_encrypt_info_t encrypt
;
746 memset(&encrypt
, 0, sizeof(encrypt
));
747 encrypt
.decrypt_gssapi_tvb
=DECRYPT_GSSAPI_NORMAL
;
749 dissect_kerberos_encrypted_message(tmp_tvb
, pinfo
, subtree
, &encrypt
);
751 if(encrypt
.gssapi_decrypted_tvb
){
752 tmp_tvb
= encrypt
.gssapi_decrypted_tvb
;
754 content_type_str
= m_info
->orig_content_type
;
755 content_info
.media_str
= m_info
->orig_parameters
;
756 } else if(encrypt
.gssapi_encrypted_tvb
) {
757 tmp_tvb
= encrypt
.gssapi_encrypted_tvb
;
758 proto_tree_add_expert(tree
, pinfo
, &ei_multipart_decryption_not_possible
, tmp_tvb
, 0, -1);
771 * Try and remove any content transfer encoding so that each sub-dissector
772 * doesn't have to do it itself
776 if(content_trans_encoding_str
&& remove_base64_encoding
) {
778 if(!g_ascii_strncasecmp(content_trans_encoding_str
, "base64", 6))
779 tmp_tvb
= base64_decode(pinfo
, tmp_tvb
, filename
? filename
: (mimetypename
? mimetypename
: content_type_str
));
783 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
784 if(content_encoding_str
&& uncompress_data
) {
786 if(g_ascii_strncasecmp(content_encoding_str
,"gzip",4) == 0 ||
787 g_ascii_strncasecmp(content_encoding_str
,"deflate",7) == 0 ||
788 g_ascii_strncasecmp(content_encoding_str
,"x-gzip",6) == 0 ||
789 g_ascii_strncasecmp(content_encoding_str
,"x-deflate",9) == 0){
790 /* The body is gzip:ed */
791 tvbuff_t
*uncompress_tvb
= tvb_child_uncompress_zlib(tmp_tvb
, tmp_tvb
, 0, body_len
);
792 if (uncompress_tvb
) {
793 tmp_tvb
= uncompress_tvb
;
794 add_new_data_source(pinfo
, tmp_tvb
, "gunzipped data");
801 * First try the dedicated multipart dissector table
803 dissected
= dissector_try_string_with_data(multipart_media_subdissector_table
,
804 content_type_str
, tmp_tvb
, pinfo
, subtree
, true, &content_info
);
807 * Fall back to the default media dissector table
809 dissected
= dissector_try_string_with_data(media_type_dissector_table
,
810 content_type_str
, tmp_tvb
, pinfo
, subtree
, true, &content_info
);
813 const char *save_match_string
= pinfo
->match_string
;
814 pinfo
->match_string
= content_type_str
;
815 call_dissector_with_data(media_handle
, tmp_tvb
, pinfo
, subtree
, &content_info
);
816 pinfo
->match_string
= save_match_string
;
818 content_info
.media_str
= NULL
; /* Shares same memory as content_type_str */
820 call_data_dissector(tmp_tvb
, pinfo
, subtree
);
822 proto_item_set_len(ti
, boundary_start
- start
);
823 if (*last_boundary
== true) {
824 proto_tree_add_item(tree
, hf_multipart_last_boundary
, tvb
, boundary_start
, boundary_line_len
, ENC_NA
|ENC_ASCII
);
826 proto_tree_add_item(tree
, hf_multipart_boundary
, tvb
, boundary_start
, boundary_line_len
, ENC_NA
|ENC_ASCII
);
829 return boundary_start
+ boundary_line_len
;
834 * Call this method to actually dissect the multipart body.
835 * NOTE - Only do so if a boundary string has been found!
837 static int dissect_multipart(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
842 media_content_info_t
*content_info
= (media_content_info_t
*)data
;
843 multipart_info_t
*m_info
= get_multipart_info(pinfo
, content_info
);
844 int header_start
= 0;
846 bool last_boundary
= false;
848 if (m_info
== NULL
) {
850 * We can't get the required multipart information
852 proto_tree_add_expert(tree
, pinfo
, &ei_multipart_no_required_parameter
, tvb
, 0, -1);
853 call_data_dissector(tvb
, pinfo
, tree
);
854 return tvb_reported_length(tvb
);
857 /* Add stuff to the protocol tree */
858 ti
= proto_tree_add_item(tree
, proto_multipart
,
860 subtree
= proto_item_add_subtree(ti
, ett_multipart
);
861 proto_item_append_text(ti
, ", Type: %s, Boundary: \"%s\"",
862 m_info
->type
, m_info
->boundary
);
864 /* Show multi-part type as a generated field */
865 type_ti
= proto_tree_add_string(subtree
, hf_multipart_type
,
866 tvb
, 0, 0, pinfo
->match_string
);
867 proto_item_set_generated(type_ti
);
870 * Make no entries in Protocol column and Info column on summary display,
871 * but stop sub-dissectors from clearing entered text in summary display.
873 col_set_fence(pinfo
->cinfo
, COL_INFO
);
876 * Process the multipart preamble
878 header_start
= process_preamble(subtree
, tvb
, m_info
, &last_boundary
);
879 if (header_start
== -1) {
880 call_data_dissector(tvb
, pinfo
, subtree
);
881 return tvb_reported_length(tvb
);
884 * Process the encapsulated bodies
886 while (last_boundary
== false) {
887 header_start
= process_body_part(subtree
, tvb
, content_info
, m_info
,
888 pinfo
, header_start
, body_index
++, &last_boundary
);
889 if (header_start
== -1) {
890 return tvb_reported_length(tvb
);
894 * Process the multipart trailer
896 if (tvb_reported_length_remaining(tvb
, header_start
) > 0) {
897 proto_tree_add_item(subtree
, hf_multipart_trailer
, tvb
, header_start
, -1, ENC_NA
);
900 return tvb_reported_length(tvb
);
903 /* Returns index of method in multipart_headers */
905 is_known_multipart_header(const char *header_str
, unsigned len
)
909 for (i
= 1; i
< array_length(multipart_headers
); i
++) {
910 if (len
== strlen(multipart_headers
[i
].name
) &&
911 g_ascii_strncasecmp(header_str
, multipart_headers
[i
].name
, len
) == 0)
913 if (multipart_headers
[i
].compact_name
!= NULL
&&
914 len
== strlen(multipart_headers
[i
].compact_name
) &&
915 g_ascii_strncasecmp(header_str
, multipart_headers
[i
].compact_name
, len
) == 0)
923 * Register the protocol with Wireshark.
925 * This format is required because a script is used to build the C function
926 * that calls all the protocol registration.
930 proto_register_multipart(void)
933 /* Setup list of header fields See Section 1.6.1 for details */
934 static hf_register_info hf
[] = {
935 { &hf_multipart_type
,
937 "mime_multipart.type",
938 FT_STRING
, BASE_NONE
, NULL
, 0x00,
939 "MIME multipart encapsulation type", HFILL
942 { &hf_multipart_part
,
943 { "Encapsulated multipart part",
944 "mime_multipart.part",
945 FT_STRING
, BASE_NONE
, NULL
, 0x00,
949 { &hf_multipart_sec_token_len
,
950 { "Length of security token",
951 "mime_multipart.header.sectoken-length",
952 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
953 "Length of the Kerberos BLOB which follows this token", HFILL
956 { &hf_header_array
[POS_CONTENT_DESCRIPTION
],
957 { "Content-Description",
958 "mime_multipart.header.content-description",
959 FT_STRING
, BASE_NONE
, NULL
, 0x00,
960 "Content-Description Header", HFILL
963 { &hf_header_array
[POS_CONTENT_DISPOSITION
],
964 { "Content-Disposition",
965 "mime_multipart.header.content-disposition",
966 FT_STRING
, BASE_NONE
, NULL
, 0x00,
967 "RFC 2183: Content-Disposition Header", HFILL
970 { &hf_header_array
[POS_CONTENT_ENCODING
],
971 { "Content-Encoding",
972 "mime_multipart.header.content-encoding",
973 FT_STRING
, BASE_NONE
, NULL
, 0x00,
974 "Content-Encoding Header", HFILL
977 { &hf_header_array
[POS_CONTENT_ID
],
979 "mime_multipart.header.content-id",
980 FT_STRING
, BASE_NONE
, NULL
, 0x00,
981 "RFC 2045: Content-Id Header", HFILL
984 { &hf_header_array
[POS_CONTENT_LANGUAGE
],
985 { "Content-Language",
986 "mime_multipart.header.content-language",
987 FT_STRING
, BASE_NONE
, NULL
, 0x00,
988 "Content-Language Header", HFILL
991 { &hf_header_array
[POS_CONTENT_LENGTH
],
993 "mime_multipart.header.content-length",
994 FT_STRING
, BASE_NONE
, NULL
, 0x0,
995 "Content-Length Header", HFILL
998 { &hf_header_array
[POS_CONTENT_TRANSFER_ENCODING
],
999 { "Content-Transfer-Encoding",
1000 "mime_multipart.header.content-transfer-encoding",
1001 FT_STRING
, BASE_NONE
, NULL
, 0x00,
1002 "RFC 2045: Content-Transfer-Encoding Header", HFILL
1005 { &hf_header_array
[POS_CONTENT_TYPE
],
1007 "mime_multipart.header.content-type",
1008 FT_STRING
, BASE_NONE
,NULL
,0x0,
1009 "Content-Type Header", HFILL
1012 { &hf_header_array
[POS_ORIGINALCONTENT
],
1013 { "OriginalContent",
1014 "mime_multipart.header.originalcontent",
1015 FT_STRING
, BASE_NONE
,NULL
,0x0,
1016 "Original Content-Type Header", HFILL
1020 /* Generated from convert_proto_tree_add_text.pl */
1021 { &hf_multipart_first_boundary
, { "First boundary", "mime_multipart.first_boundary", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
1022 { &hf_multipart_preamble
, { "Preamble", "mime_multipart.preamble", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
1023 { &hf_multipart_last_boundary
, { "Last boundary", "mime_multipart.last_boundary", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
1024 { &hf_multipart_boundary
, { "Boundary", "mime_multipart.boundary", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
1025 { &hf_multipart_trailer
, { "Trailer", "mime_multipart.trailer", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
1032 module_t
*multipart_module
;
1033 expert_module_t
* expert_multipart
;
1037 * Setup protocol subtree array
1039 static int *ett
[] = {
1041 &ett_multipart_main
,
1042 &ett_multipart_body
,
1045 static ei_register_info ei
[] = {
1046 { &ei_multipart_no_required_parameter
, { "mime_multipart.no_required_parameter", PI_PROTOCOL
, PI_ERROR
, "The multipart dissector could not find a required parameter.", EXPFILL
}},
1047 { &ei_multipart_decryption_not_possible
, { "mime_multipart.decryption_not_possible", PI_UNDECODED
, PI_WARN
, "The multipart dissector could not decrypt the message.", EXPFILL
}},
1051 * Register the protocol name and description
1053 proto_multipart
= proto_register_protocol("MIME Multipart Media Encapsulation", "MIME multipart", "mime_multipart");
1056 * Required function calls to register
1057 * the header fields and subtrees used.
1059 proto_register_field_array(proto_multipart
, hf
, array_length(hf
));
1060 proto_register_subtree_array(ett
, array_length(ett
));
1061 expert_multipart
= expert_register_protocol(proto_multipart
);
1062 expert_register_field_array(expert_multipart
, ei
, array_length(ei
));
1064 multipart_module
= prefs_register_protocol(proto_multipart
, NULL
);
1066 prefs_register_bool_preference(multipart_module
,
1067 "display_unknown_body_as_text",
1068 "Display bodies without media type as text",
1069 "Display multipart bodies with no media type dissector"
1070 " as raw text (may cause problems with binary data).",
1071 &display_unknown_body_as_text
);
1073 prefs_register_bool_preference(multipart_module
,
1074 "remove_base64_encoding",
1075 "Remove base64 encoding from bodies",
1076 "Remove any base64 content-transfer encoding from bodies. "
1077 "This supports export of the body and its further dissection.",
1078 &remove_base64_encoding
);
1080 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
1081 prefs_register_bool_preference(multipart_module
,
1083 "Uncompress parts which are compressed",
1084 "Uncompress parts which are compressed. GZIP for example. "
1085 "This supports export of the body and its further dissection.",
1090 * Dissectors requiring different behavior in cases where the media
1091 * is contained in a multipart entity should register their multipart
1092 * dissector in the dissector table below, which is similar to the
1093 * "media_type" dissector table defined in the HTTP dissector code.
1095 multipart_media_subdissector_table
= register_dissector_table(
1096 "multipart_media_type",
1097 "Internet media type (for multipart processing)",
1098 proto_multipart
, FT_STRING
, STRING_CASE_INSENSITIVE
);
1101 * Handle for multipart dissection
1103 multipart_handle
= register_dissector("mime_multipart",
1104 dissect_multipart
, proto_multipart
);
1108 /* If this dissector uses sub-dissector registration add a registration routine.
1109 This format is required because a script is used to find these routines and
1110 create the code that calls these routines.
1113 proto_reg_handoff_multipart(void)
1116 * When we cannot display the data, call the data dissector.
1117 * When there is no dissector for the given media, call the media dissector.
1119 media_handle
= find_dissector_add_dependency("media", proto_multipart
);
1120 gssapi_handle
= find_dissector_add_dependency("gssapi", proto_multipart
);
1123 * Get the content type and Internet media type table
1125 media_type_dissector_table
= find_dissector_table("media_type");
1127 dissector_add_string("media_type",
1128 "multipart/mixed", multipart_handle
);
1129 dissector_add_string("media_type",
1130 "multipart/related", multipart_handle
);
1131 dissector_add_string("media_type",
1132 "multipart/alternative", multipart_handle
);
1133 dissector_add_string("media_type",
1134 "multipart/form-data", multipart_handle
);
1135 dissector_add_string("media_type",
1136 "multipart/report", multipart_handle
);
1137 dissector_add_string("media_type",
1138 "multipart/signed", multipart_handle
);
1139 dissector_add_string("media_type",
1140 "multipart/encrypted", multipart_handle
);
1143 * Supply an entry to use for unknown multipart subtype.
1144 * See RFC 2046, section 5.1.3
1146 dissector_add_string("media_type",
1147 "multipart/", multipart_handle
);
1151 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1156 * indent-tabs-mode: nil
1159 * vi: set shiftwidth=4 tabstop=8 expandtab:
1160 * :indentSize=4:tabSize=8:noTabs=true: