3 * Routines for JFIF image/jpeg media dissection
4 * Copyright 2004, Olivier Biot.
8 * Refer to the AUTHORS file or the AUTHORS section in the man page
9 * for contacting the author(s) of this file.
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * JFIF media decoding functionality provided by Olivier Biot.
17 * The JFIF specifications are found at several locations, such as:
18 * http://www.jpeg.org/public/jfif.pdf
19 * http://www.w3.org/Graphics/JPEG/itu-t81.pdf
21 * The Exif specifications are found at several locations, such as:
22 * http://www.exif.org/
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * as published by the Free Software Foundation; either version 2
27 * of the License, or (at your option) any later version.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * You should have received a copy of the GNU General Public License
35 * along with this program; if not, write to the Free Software
36 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
39 /* Edit this file with 4-space tabulation */
51 #include <epan/packet.h>
53 /* General-purpose debug logger.
54 * Requires double parentheses because of variable arguments of printf().
56 * Enable debug logging for JFIF by defining AM_CFLAGS
57 * so that it contains "-DDEBUG_image_jfif" or "-DDEBUG_image"
59 #if (defined(DEBUG_image_jfif) || defined(DEBUG_image))
61 g_print("%s:%u: ", __FILE__, __LINE__); \
67 #define IMG_JFIF "image-jfif"
69 /************************** Variable declarations **************************/
71 #define MARKER_TEM 0xFF01
73 /* 0xFF02 -- 0xFFBF are reserved */
75 #define MARKER_SOF0 0xFFC0
76 #define MARKER_SOF1 0xFFC1
77 #define MARKER_SOF2 0xFFC2
78 #define MARKER_SOF3 0xFFC3
80 #define MARKER_DHT 0xFFC4
82 #define MARKER_SOF5 0xFFC5
83 #define MARKER_SOF6 0xFFC6
84 #define MARKER_SOF7 0xFFC7
85 #define MARKER_SOF8 0xFFC8
86 #define MARKER_SOF9 0xFFC9
87 #define MARKER_SOF10 0xFFCA
88 #define MARKER_SOF11 0xFFCB
90 #define MARKER_DAC 0xFFCC
92 #define MARKER_SOF13 0xFFCD
93 #define MARKER_SOF14 0xFFCE
94 #define MARKER_SOF15 0xFFCF
96 #define MARKER_RST0 0xFFD0
97 #define MARKER_RST1 0xFFD1
98 #define MARKER_RST2 0xFFD2
99 #define MARKER_RST3 0xFFD3
100 #define MARKER_RST4 0xFFD4
101 #define MARKER_RST5 0xFFD5
102 #define MARKER_RST6 0xFFD6
103 #define MARKER_RST7 0xFFD7
105 #define MARKER_FFDB 0xFFDB
106 #define MARKER_SOI 0xFFD8
107 #define MARKER_EOI 0xFFD9
108 #define MARKER_SOS 0xFFDA
109 #define MARKER_DQT 0xFFDB
110 #define MARKER_DNL 0xFFDC
111 #define MARKER_DRI 0xFFDD
112 #define MARKER_DHP 0xFFDE
113 #define MARKER_EXP 0xFFDF
115 #define MARKER_APP0 0xFFE0
116 #define MARKER_APP1 0xFFE1
117 #define MARKER_APP2 0xFFE2
118 #define MARKER_APP3 0xFFE3
119 #define MARKER_APP4 0xFFE4
120 #define MARKER_APP5 0xFFE5
121 #define MARKER_APP6 0xFFE6
122 #define MARKER_APP7 0xFFE7
123 #define MARKER_APP8 0xFFE8
124 #define MARKER_APP9 0xFFE9
125 #define MARKER_APP10 0xFFEA
126 #define MARKER_APP11 0xFFEB
127 #define MARKER_APP12 0xFFEC
128 #define MARKER_APP13 0xFFED
129 #define MARKER_APP14 0xFFEE
130 #define MARKER_APP15 0xFFEF
132 #define MARKER_JPG0 0xFFF0
133 #define MARKER_JPG1 0xFFF1
134 #define MARKER_JPG2 0xFFF2
135 #define MARKER_JPG3 0xFFF3
136 #define MARKER_JPG4 0xFFF4
137 #define MARKER_JPG5 0xFFF5
138 #define MARKER_JPG6 0xFFF6
139 #define MARKER_JPG7 0xFFF7
140 #define MARKER_JPG8 0xFFF8
141 #define MARKER_JPG9 0xFFF9
142 #define MARKER_JPG10 0xFFFA
143 #define MARKER_JPG11 0xFFFB
144 #define MARKER_JPG12 0xFFFC
145 #define MARKER_JPG13 0xFFFD
147 #define MARKER_COM 0xFFFE
149 #define marker_has_length(marker) ( ! ( \
150 ((marker) == MARKER_TEM) \
151 || ((marker) == MARKER_SOI) \
152 || ((marker) == MARKER_EOI) \
153 || ( ((marker) >= MARKER_RST0) && ((marker) <= MARKER_RST7) ) \
157 static const value_string vals_marker[] = {
158 { MARKER_TEM, "Reserved - For temporary private use in arithmetic coding" },
159 { MARKER_SOF0, "Start of Frame (non-differential, Huffman coding) - Baseline DCT" },
160 { MARKER_SOF1, "Start of Frame (non-differential, Huffman coding) - Extended sequential DCT" },
161 { MARKER_SOF2, "Start of Frame (non-differential, Huffman coding) - Progressive DCT" },
162 { MARKER_SOF3, "Start of Frame (non-differential, Huffman coding) - Lossless (sequential)" },
163 { MARKER_DHT, "Define Huffman table(s)" },
164 { MARKER_SOF5, "Start of Frame (differential, Huffman coding) - Differential sequential DCT" },
165 { MARKER_SOF6, "Start of Frame (differential, Huffman coding) - Differential progressive DCT" },
166 { MARKER_SOF7, "Start of Frame (differential, Huffman coding) - Differential lossless (sequential)" },
167 { MARKER_SOF8, "Start of Frame (non-differential, arithmetic coding) - Reserved for JPEG extensions" },
168 { MARKER_SOF9, "Start of Frame (non-differential, arithmetic coding) - Extended sequential DCT" },
169 { MARKER_SOF10, "Start of Frame (non-differential, arithmetic coding) - Progressive DCT" },
170 { MARKER_SOF11, "Start of Frame (non-differential, arithmetic coding) - Lossless (sequential)" },
171 { MARKER_DAC, "Define arithmetic coding conditioning(s)" },
172 { MARKER_SOF13, "Start of Frame (differential, arithmetic coding) - Differential sequential DCT" },
173 { MARKER_SOF14, "Start of Frame (differential, arithmetic coding) - Differential progressive DCT" },
174 { MARKER_SOF15, "Start of Frame (differential, arithmetic coding) - Differential lossless (sequential)" },
175 { MARKER_RST0, "Restart interval termination - Restart with modulo 8 count 0" },
176 { MARKER_RST1, "Restart interval termination - Restart with modulo 8 count 1" },
177 { MARKER_RST2, "Restart interval termination - Restart with modulo 8 count 2" },
178 { MARKER_RST3, "Restart interval termination - Restart with modulo 8 count 3" },
179 { MARKER_RST4, "Restart interval termination - Restart with modulo 8 count 4" },
180 { MARKER_RST5, "Restart interval termination - Restart with modulo 8 count 5" },
181 { MARKER_RST6, "Restart interval termination - Restart with modulo 8 count 6" },
182 { MARKER_RST7, "Restart interval termination - Restart with modulo 8 count 7" },
183 { MARKER_SOI, "Start of Image" },
184 { MARKER_EOI, "End of Image" },
185 { MARKER_SOS, "Start of Scan" },
186 { MARKER_DQT, "Define quantization table(s)" },
187 { MARKER_DNL, "Define number of lines" },
188 { MARKER_DRI, "Define restart interval" },
189 { MARKER_DHP, "Define hierarchical progression" },
190 { MARKER_EXP, "Expand reference component(s)" },
191 { MARKER_APP0, "Reserved for application segments - 0" },
192 { MARKER_APP1, "Reserved for application segments - 1" },
193 { MARKER_APP2, "Reserved for application segments - 2" },
194 { MARKER_APP3, "Reserved for application segments - 3" },
195 { MARKER_APP4, "Reserved for application segments - 4" },
196 { MARKER_APP5, "Reserved for application segments - 5" },
197 { MARKER_APP6, "Reserved for application segments - 6" },
198 { MARKER_APP7, "Reserved for application segments - 7" },
199 { MARKER_APP8, "Reserved for application segments - 8" },
200 { MARKER_APP9, "Reserved for application segments - 9" },
201 { MARKER_APP10, "Reserved for application segments - 10" },
202 { MARKER_APP11, "Reserved for application segments - 11" },
203 { MARKER_APP12, "Reserved for application segments - 12" },
204 { MARKER_APP13, "Reserved for application segments - 13" },
205 { MARKER_APP14, "Reserved for application segments - 14" },
206 { MARKER_APP15, "Reserved for application segments - 15" },
207 { MARKER_JPG0, "Reserved for JPEG extensions - 0" },
208 { MARKER_JPG1, "Reserved for JPEG extensions - 1" },
209 { MARKER_JPG2, "Reserved for JPEG extensions - 2" },
210 { MARKER_JPG3, "Reserved for JPEG extensions - 3" },
211 { MARKER_JPG4, "Reserved for JPEG extensions - 4" },
212 { MARKER_JPG5, "Reserved for JPEG extensions - 5" },
213 { MARKER_JPG6, "Reserved for JPEG extensions - 6" },
214 { MARKER_JPG7, "Reserved for JPEG extensions - 7" },
215 { MARKER_JPG8, "Reserved for JPEG extensions - 8" },
216 { MARKER_JPG9, "Reserved for JPEG extensions - 9" },
217 { MARKER_JPG10, "Reserved for JPEG extensions - 10" },
218 { MARKER_JPG11, "Reserved for JPEG extensions - 11" },
219 { MARKER_JPG12, "Reserved for JPEG extensions - 12" },
220 { MARKER_JPG13, "Reserved for JPEG extensions - 13" },
221 { MARKER_COM, "Comment" },
225 static const value_string vals_units[] = {
226 { 0, "No units; Xdensity and Ydensity specify the pixel aspect ratio" },
227 { 1, "Dots per inch" },
228 { 2, "Dots per centimeter" },
232 static const value_string vals_extension_code[] = {
233 { 0x10, "Thumbnail encoded using JPEG" },
234 { 0x11, "Thumbnail encoded using 1 byte (8 bits) per pixel" },
235 { 0x13, "Thumbnail encoded using 3 bytes (24 bits) per pixel" },
239 static const value_string vals_exif_tags[] = {
241 * Tags related to image data structure:
243 { 0x0100, "ImageWidth" },
244 { 0x0101, "ImageLength" },
245 { 0x0102, "BitsPerSample" },
246 { 0x0103, "Compression" },
247 { 0x0106, "PhotometricInterpretation" },
248 { 0x0112, "Orientation" },
249 { 0x0115, "SamplesPerPixel" },
250 { 0x011C, "PlanarConfiguration" },
251 { 0x0212, "YCbCrSubSampling" },
252 { 0x0213, "YCbCrPositioning" },
253 { 0x011A, "XResolution" },
254 { 0x011B, "YResolution" },
255 { 0x0128, "ResolutionUnit" },
257 * Tags relating to recording offset:
259 { 0x0111, "StripOffsets" },
260 { 0x0116, "RowsPerStrip" },
261 { 0x0117, "StripByteCounts" },
262 { 0x0201, "JPEGInterchangeFormat" },
263 { 0x0202, "JPEGInterchangeFormatLength" },
265 * Tags relating to image data characteristics:
267 { 0x012D, "TransferFunction" },
268 { 0x013E, "WhitePoint" },
269 { 0x013F, "PrimaryChromaticities" },
270 { 0x0211, "YCbCrCoefficients" },
271 { 0x0214, "ReferenceBlackWhite" },
275 { 0x0132, "DateTime" },
276 { 0x010E, "ImageDescription" },
279 { 0x0131, "Software" },
280 { 0x013B, "Artist" },
281 { 0x8296, "Copyright" },
285 { 0x8769, "Exif IFD Pointer"},
286 { 0x8825, "GPS IFD Pointer"},
287 { 0xA005, "Interoperability IFD Pointer"},
292 static const value_string vals_exif_types[] = {
297 { 0x0005, "RATIONAL" },
299 { 0x0007, "UNDEFINED" },
302 { 0x000A, "SRATIONAL" },
307 /* Initialize the protocol and registered fields */
308 static int proto_jfif = -1;
311 static gint hf_marker = -1;
313 static gint hf_marker_segment = -1;
314 static gint hf_len = -1;
316 static gint hf_identifier = -1;
317 /* MARKER_APP0 - JFIF */
318 static gint hf_version = -1;
319 static gint hf_version_major = -1;
320 static gint hf_version_minor = -1;
321 static gint hf_units = -1;
322 static gint hf_xdensity = -1;
323 static gint hf_ydensity = -1;
324 static gint hf_xthumbnail = -1;
325 static gint hf_ythumbnail = -1;
326 static gint hf_rgb = -1;
327 /* MARKER_APP0 - JFXX */
328 static gint hf_extension_code = -1;
330 static gint hf_sof_header = -1;
331 static gint hf_sof_precision = -1;
332 static gint hf_sof_lines = -1;
333 static gint hf_sof_samples_per_line = -1;
334 static gint hf_sof_nf = -1;
335 static gint hf_sof_c_i = -1;
336 static gint hf_sof_h_i = -1;
337 static gint hf_sof_v_i = -1;
338 static gint hf_sof_tq_i = -1;
341 static gint hf_sos_header = -1;
342 static gint hf_sos_ns = -1;
343 static gint hf_sos_cs_j = -1;
344 static gint hf_sos_td_j = -1;
345 static gint hf_sos_ta_j = -1;
346 static gint hf_sos_ss = -1;
347 static gint hf_sos_se = -1;
348 static gint hf_sos_ah = -1;
349 static gint hf_sos_al = -1;
351 /* Initialize the subtree pointers */
352 static gint ett_jfif = -1;
353 static gint ett_marker_segment = -1;
354 static gint ett_details = -1;
357 /****************** JFIF protocol dissection functions ******************/
359 #define ErrorInvalidJFIF "This is not a valid JFIF (JPEG) object"
363 * Process a marker segment (with length).
366 process_marker_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
367 guint16 marker, const char *marker_name)
369 proto_item *ti = NULL;
370 proto_tree *subtree = NULL;
375 ti = proto_tree_add_item(tree, hf_marker_segment,
377 subtree = proto_item_add_subtree(ti, ett_marker_segment);
379 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
380 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
382 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
384 proto_tree_add_text(subtree, tvb, 4, -1,
385 "Remaining segment data (%u bytes)", len - 2);
389 * Process a Start of Frame header (with length).
392 process_sof_header(proto_tree *tree, tvbuff_t *tvb, guint32 len _U_,
393 guint16 marker, const char *marker_name)
395 proto_item *ti = NULL;
396 proto_tree *subtree = NULL;
401 ti = proto_tree_add_item(tree, hf_sof_header,
403 subtree = proto_item_add_subtree(ti, ett_marker_segment);
405 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
406 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
408 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
410 proto_tree_add_item(subtree, hf_sof_precision, tvb, 4, 1, FALSE);
412 proto_tree_add_item(subtree, hf_sof_lines, tvb, 5, 2, FALSE);
414 proto_tree_add_item(subtree, hf_sof_samples_per_line, tvb, 7, 2, FALSE);
416 proto_tree_add_item(subtree, hf_sof_nf, tvb, 9, 1, FALSE);
418 guint8 count = tvb_get_guint8(tvb, 9);
421 proto_tree_add_item(subtree, hf_sof_c_i, tvb, offset++, 1, FALSE);
422 proto_tree_add_item(subtree, hf_sof_h_i, tvb, offset, 1, FALSE);
423 proto_tree_add_item(subtree, hf_sof_v_i, tvb, offset++, 1, FALSE);
424 proto_tree_add_item(subtree, hf_sof_tq_i, tvb, offset++, 1, FALSE);
431 * Process a Start of Segment header (with length).
434 process_sos_header(proto_tree *tree, tvbuff_t *tvb, guint32 len _U_,
435 guint16 marker, const char *marker_name)
437 proto_item *ti = NULL;
438 proto_tree *subtree = NULL;
444 ti = proto_tree_add_item(tree, hf_sos_header,
446 subtree = proto_item_add_subtree(ti, ett_marker_segment);
448 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
449 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
451 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
453 proto_tree_add_item(subtree, hf_sos_ns, tvb, 4, 1, FALSE);
455 guint8 count = tvb_get_guint8(tvb, 4);
458 proto_tree_add_item(subtree, hf_sos_cs_j, tvb, offset++, 1, FALSE);
459 proto_tree_add_item(subtree, hf_sos_td_j, tvb, offset, 1, FALSE);
460 proto_tree_add_item(subtree, hf_sos_ta_j, tvb, offset++, 1, FALSE);
465 proto_tree_add_item(subtree, hf_sos_ss, tvb, offset++, 1, FALSE);
466 proto_tree_add_item(subtree, hf_sos_se, tvb, offset++, 1, FALSE);
468 proto_tree_add_item(subtree, hf_sos_ah, tvb, offset, 1, FALSE);
469 proto_tree_add_item(subtree, hf_sos_al, tvb, offset++, 1, FALSE);
472 /* Process an APP0 block.
474 * XXX - This code only works on US-ASCII systems!!!
477 process_app0_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
478 guint16 marker, const char *marker_name)
480 proto_item *ti = NULL;
481 proto_tree *subtree = NULL;
482 proto_tree *subtree_details = NULL;
490 ti = proto_tree_add_item(tree, hf_marker_segment,
492 subtree = proto_item_add_subtree(ti, ett_marker_segment);
494 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
495 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
497 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
499 str = tvb_get_ephemeral_stringz(tvb, 4, &str_size);
500 ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE);
501 if (strcmp(str, "JFIF") == 0) {
503 ti = proto_tree_add_none_format(subtree, hf_version,
504 tvb, 9, 2, "Version: %u.%u",
505 tvb_get_guint8(tvb, 9),
506 tvb_get_guint8(tvb, 10));
507 subtree_details = proto_item_add_subtree(ti, ett_details);
508 proto_tree_add_item(subtree_details, hf_version_major,
510 proto_tree_add_item(subtree_details, hf_version_minor,
513 proto_tree_add_item(subtree, hf_units,
517 proto_tree_add_item(subtree, hf_xdensity,
519 proto_tree_add_item(subtree, hf_ydensity,
523 proto_tree_add_item(subtree, hf_xthumbnail,
525 proto_tree_add_item(subtree, hf_ythumbnail,
528 guint16 x = tvb_get_guint8(tvb, 16);
529 guint16 y = tvb_get_guint8(tvb, 17);
531 proto_tree_add_item(subtree, hf_rgb,
532 tvb, 18, 3 * (x * y), FALSE);
533 offset = 18 + (3 * (x * y));
538 } else if (strcmp(str, "JFXX") == 0) {
539 proto_tree_add_item(subtree, hf_extension_code,
542 guint8 code = tvb_get_guint8(tvb, 9);
544 case 0x10: /* Thumbnail coded using JPEG */
546 case 0x11: /* thumbnail stored using 1 byte per pixel */
548 case 0x13: /* thumbnail stored using 3 bytes per pixel */
554 } else { /* Unknown */
555 proto_item_append_text(ti, " (unknown identifier)");
556 offset = 4 + str_size;
558 proto_tree_add_text(subtree, tvb, offset, -1,
559 "Remaining segment data (%u bytes)", len - 2 - str_size);
564 /* Process an APP1 block.
566 * XXX - This code only works on US-ASCII systems!!!
569 process_app1_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
570 guint16 marker, const char *marker_name)
572 proto_item *ti = NULL;
573 proto_tree *subtree = NULL;
582 ti = proto_tree_add_item(tree, hf_marker_segment,
584 subtree = proto_item_add_subtree(ti, ett_marker_segment);
586 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
587 proto_tree_add_item(subtree, hf_marker, tvb, offset, 2, FALSE);
590 proto_tree_add_item(subtree, hf_len, tvb, offset, 2, FALSE);
593 str = tvb_get_ephemeral_stringz(tvb, offset, &str_size);
594 ti = proto_tree_add_item(subtree, hf_identifier, tvb, offset, str_size, FALSE);
596 if (strcmp(str, "Exif") == 0) {
600 gboolean is_little_endian;
605 offset++; /* Skip a byte supposed to be 0x00 */
608 val_16 = tvb_get_ntohs(tvb, offset);
609 if (val_16 == 0x4949) {
610 is_little_endian = TRUE;
611 proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: little endian");
612 } else if (val_16 == 0x4D4D) {
613 is_little_endian = FALSE;
614 proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: big endian");
616 /* Error: invalid endianness encoding */
617 proto_tree_add_text(subtree, tvb, offset, 2,
618 "Incorrect endianness encoding - skipping the remainder of this application marker");
623 * Fixed value 42 = 0x002a
629 if (is_little_endian) {
630 val_32 = tvb_get_letohl(tvb, offset);
632 val_32 = tvb_get_ntohl(tvb, offset);
635 * Check for a bogus val_32 value.
636 * XXX - bogus value message should also deal with a
637 * value that's too large and causes an overflow.
638 * Or should it just check against the segment length,
641 if (val_32 + tiff_start < (guint32)offset + 4) {
642 proto_tree_add_text(subtree, tvb, offset, 4,
643 "Start offset of IFD starting from the TIFF header start: %u bytes (bogus, should be >= %u",
644 val_32, offset + 4 - tiff_start);
647 proto_tree_add_text(subtree, tvb, offset, 4,
648 "Start offset of IFD starting from the TIFF header start: %u bytes",
652 * Skip the following portion
654 if (val_32 + tiff_start > (guint32)offset) {
655 proto_tree_add_text(subtree, tvb, offset, val_32 + tiff_start - offset,
656 "Skipped data between end of TIFF header and start of IFD (%u bytes)",
657 val_32 + tiff_start - offset);
660 offset = val_32 + tiff_start;
664 if (is_little_endian) {
665 num_fields = tvb_get_letohs(tvb, offset);
667 num_fields = tvb_get_ntohs(tvb, offset);
669 proto_tree_add_text(subtree, tvb, offset, 2, "Number of fields in this IFD: %u", num_fields);
671 while (num_fields-- > 0) {
675 if (is_little_endian) {
676 tag = tvb_get_letohs(tvb, offset);
677 type = tvb_get_letohs(tvb, offset + 2);
678 count = tvb_get_letohl(tvb, offset + 4);
679 off = tvb_get_letohl(tvb, offset + 8);
681 tag = tvb_get_ntohs(tvb, offset);
682 type = tvb_get_ntohs(tvb, offset + 2);
683 count = tvb_get_ntohl(tvb, offset + 4);
684 off = tvb_get_ntohl(tvb, offset + 8);
686 /* TODO - refine this */
687 proto_tree_add_text(subtree, tvb, offset, 2,
688 "Exif Tag: 0x%04X (%s), Type: %u (%s), Count: %u, "
689 "Value offset from start of TIFF header: %u",
690 tag, val_to_str(tag, vals_exif_tags, "Unknown Exif tag"),
691 type, val_to_str(type, vals_exif_types, "Unknown Exif type"),
696 * Offset to the next IFD
698 if (is_little_endian) {
699 val_32 = tvb_get_letohl(tvb, offset);
701 val_32 = tvb_get_ntohl(tvb, offset);
704 val_32 + tiff_start < (guint32)offset + 4) {
705 proto_tree_add_text(subtree, tvb, offset, 4,
706 "Offset to next IFD from start of TIFF header: %u bytes (bogus, should be >= %u)",
707 val_32, offset + 4 - tiff_start);
710 proto_tree_add_text(subtree, tvb, offset, 4,
711 "Offset to next IFD from start of TIFF header: %u bytes",
718 proto_tree_add_text(subtree, tvb, offset, -1,
719 "Remaining segment data (%u bytes)", len - 2 - str_size);
720 proto_item_append_text(ti, " (Unknown identifier)");
724 /* Process an APP2 block.
726 * XXX - This code only works on US-ASCII systems!!!
729 process_app2_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
730 guint16 marker, const char *marker_name)
732 proto_item *ti = NULL;
733 proto_tree *subtree = NULL;
740 ti = proto_tree_add_item(tree, hf_marker_segment,
742 subtree = proto_item_add_subtree(ti, ett_marker_segment);
744 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
745 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
747 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
749 str = tvb_get_ephemeral_stringz(tvb, 4, &str_size);
750 ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE);
751 if (strcmp(str, "FPXR") == 0) {
752 proto_tree_add_text(tree, tvb, 0, -1, "Exif FlashPix APP2 application marker");
754 proto_tree_add_text(subtree, tvb, 4 + str_size, -1,
755 "Remaining segment data (%u bytes)", len - 2 - str_size);
756 proto_item_append_text(ti, " (Unknown identifier)");
761 dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
763 proto_tree *subtree = NULL;
764 proto_item *ti = NULL;
765 guint tvb_len = tvb_reported_length(tvb);
768 /* check if we have a full JFIF in tvb */
771 if (tvb_get_ntohs(tvb, 0) != MARKER_SOI)
773 if (tvb_get_ntohs(tvb, tvb_len-2) != MARKER_EOI)
776 /* Add summary to INFO column if it is enabled */
777 if (check_col(pinfo->cinfo, COL_INFO))
778 col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", "(JPEG JFIF image)");
781 ti = proto_tree_add_item(tree, proto_jfif,
783 subtree = proto_item_add_subtree(ti, ett_jfif);
784 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
787 offset = 2; /* skip MARKER_SOI */
790 * Process the remaining markers and marker segments
792 while (offset < tvb_len) {
794 const guint16 marker = tvb_get_ntohs(tvb, offset);
795 str = match_strval(marker, vals_marker);
796 if (str) { /* Known marker */
797 if (marker_has_length(marker)) { /* Marker segment */
798 /* Length of marker segment = 2 + len */
799 const guint16 len = tvb_get_ntohs(tvb, offset + 2);
800 tvbuff_t *tmp_tvb = tvb_new_subset(tvb, offset, 2 + len, 2 + len);
803 process_app0_segment(subtree, tmp_tvb, len, marker, str);
806 process_app1_segment(subtree, tmp_tvb, len, marker, str);
809 process_app2_segment(subtree, tmp_tvb, len, marker, str);
825 process_sof_header(subtree, tmp_tvb, len, marker, str);
828 process_sos_header(subtree, tmp_tvb, len, marker, str);
830 * TODO - dissect a scan; the scan data is encoded.
832 proto_tree_add_text(subtree, tvb, offset + 2 + len, -1,
833 "JFIF dissection stops here (dissection of a scan is not yet implemented)");
837 process_marker_segment(subtree, tmp_tvb, len, marker, str);
841 } else { /* Marker but no segment */
843 proto_tree_add_item(subtree, hf_marker,
844 tvb, offset, 2, FALSE);
847 } else { /* Reserved! */
848 ti = proto_tree_add_item(subtree, hf_marker,
849 tvb, offset, 2, FALSE);
850 proto_item_append_text(ti, " (Reserved)");
859 dissect_jfif_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
861 return dissect_jfif(tvb, pinfo, tree) > 0;
864 /****************** Register the protocol with Wireshark ******************/
867 /* This format is required because a script is used to build the C function
868 * that calls the protocol registration. */
871 proto_register_jfif(void)
874 * Setup list of header fields.
876 static hf_register_info hf[] = {
881 FT_UINT8, BASE_HEX, VALS(vals_marker), 0x00,
887 { &hf_marker_segment,
889 IMG_JFIF ".marker_segment",
890 FT_NONE, BASE_NONE, NULL, 0x00,
898 FT_UINT16, BASE_DEC, 0, 0x00,
899 "Length of segment (including length field)",
906 IMG_JFIF ".identifier",
907 FT_STRINGZ, BASE_NONE, NULL, 0x00,
908 "Identifier of the segment",
912 /* MARKER_APP0 - JFIF */
916 FT_NONE, BASE_NONE, NULL, 0x00,
923 IMG_JFIF ".version.major",
924 FT_UINT8, BASE_DEC, NULL, 0x00,
925 "JFIF Major Version",
931 IMG_JFIF ".version.minor",
932 FT_UINT8, BASE_DEC, NULL, 0x00,
933 "JFIF Minor Version",
940 FT_UINT8, BASE_DEC, VALS(vals_units), 0x00,
941 "Units used in this segment",
947 IMG_JFIF ".Xdensity",
948 FT_UINT16, BASE_DEC, NULL, 0x00,
949 "Horizontal pixel density",
955 IMG_JFIF ".Ydensity",
956 FT_UINT16, BASE_DEC, NULL, 0x00,
957 "Vertical pixel density",
963 IMG_JFIF ".Xthumbnail",
964 FT_UINT16, BASE_DEC, NULL, 0x00,
965 "Thumbnail horizontal pixel count",
971 IMG_JFIF ".Ythumbnail",
972 FT_UINT16, BASE_DEC, NULL, 0x00,
973 "Thumbnail vertical pixel count",
978 { "RGB values of thumbnail pixels",
980 FT_BYTES, BASE_NONE, NULL, 0x00,
981 "RGB values of the thumbnail pixels (24 bit per pixel, Xthumbnail x Ythumbnail pixels)",
985 /* MARKER_APP0 - JFXX */
986 { &hf_extension_code,
988 IMG_JFIF ".extension.code",
989 FT_UINT8, BASE_HEX, VALS(vals_extension_code), 0x00,
990 "JFXX extension code for thumbnail encoding",
994 /* Header: Start of Frame (MARKER_SOF) */
996 { "Start of Frame header",
998 FT_NONE, BASE_NONE, NULL, 0x00,
1003 { &hf_sof_precision,
1004 { "Sample Precision (bits)",
1005 IMG_JFIF ".sof.precision",
1006 FT_UINT8, BASE_DEC, NULL, 0x00,
1007 "Specifies the precision in bits for the samples of the components in the frame.",
1013 IMG_JFIF ".sof.lines",
1014 FT_UINT16, BASE_DEC, NULL, 0x00,
1015 "Specifies the maximum number of lines in the source image.",
1019 { &hf_sof_samples_per_line,
1020 { "Samples per line",
1021 IMG_JFIF ".sof.samples_per_line",
1022 FT_UINT16, BASE_DEC, NULL, 0x00,
1023 "Specifies the maximum number of samples per line in the source image.",
1028 { "Number of image components in frame",
1030 FT_UINT8, BASE_DEC, NULL, 0x00,
1031 "Specifies the number of source image components in the frame.",
1036 { "Component identifier",
1037 IMG_JFIF ".sof.c_i",
1038 FT_UINT8, BASE_DEC, NULL, 0x00,
1039 "Assigns a unique label to the ith component in the sequence of frame component specification parameters.",
1044 { "Horizontal sampling factor",
1045 IMG_JFIF ".sof.h_i",
1046 FT_UINT8, BASE_DEC, NULL, 0xF0,
1047 "Specifies the relationship between the component horizontal dimension and maximum image dimension X.",
1052 { "Vertical sampling factor",
1053 IMG_JFIF ".sof.v_i",
1054 FT_UINT8, BASE_DEC, NULL, 0x0F,
1055 "Specifies the relationship between the component vertical dimension and maximum image dimension Y.",
1060 { "Quantization table destination selector",
1061 IMG_JFIF ".sof.tq_i",
1062 FT_UINT8, BASE_DEC, NULL, 0x00,
1063 "Specifies one of four possible quantization table destinations from which the quantization table to use for dequantization of DCT coefficients of component Ci is retrieved.",
1068 /* Header: Start of Segment (MARKER_SOS) */
1070 { "Start of Segment header",
1071 IMG_JFIF ".header.sos",
1072 FT_NONE, BASE_NONE, NULL, 0x00,
1078 { "Number of image components in scan",
1080 FT_UINT8, BASE_DEC, NULL, 0x00,
1081 "Specifies the number of source image components in the scan.",
1086 { "Scan component selector",
1087 IMG_JFIF ".sos.component_selector",
1088 FT_UINT8, BASE_DEC, NULL, 0x00,
1089 "Selects which of the Nf image components specified in the frame parameters shall be the jth component in the scan.",
1094 { "DC entropy coding table destination selector",
1095 IMG_JFIF ".sos.dc_entropy_selector",
1096 FT_UINT8, BASE_DEC, NULL, 0xF0,
1097 "Specifies one of four possible DC entropy coding table destinations from which the entropy table needed for decoding of the DC coefficients of component Csj is retrieved.",
1102 { "AC entropy coding table destination selector",
1103 IMG_JFIF ".sos.ac_entropy_selector",
1104 FT_UINT8, BASE_DEC, NULL, 0x0F,
1105 "Specifies one of four possible AC entropy coding table destinations from which the entropy table needed for decoding of the AC coefficients of component Csj is retrieved.",
1110 { "Start of spectral or predictor selection",
1112 FT_UINT8, BASE_DEC, NULL, 0x00,
1113 "In the DCT modes of operation, this parameter specifies the first DCT coefficient in each block in zig-zag order which shall be coded in the scan. This parameter shall be set to zero for the sequential DCT processes. In the lossless mode of operations this parameter is used to select the predictor.",
1118 { "End of spectral selection",
1120 FT_UINT8, BASE_DEC, NULL, 0x00,
1121 "Specifies the last DCT coefficient in each block in zig-zag order which shall be coded in the scan. This parameter shall be set to 63 for the sequential DCT processes. In the lossless mode of operations this parameter has no meaning. It shall be set to zero.",
1126 { "Successive approximation bit position high",
1128 FT_UINT8, BASE_DEC, NULL, 0xF0,
1129 "This parameter specifies the point transform used in the preceding scan (i.e. successive approximation bit position low in the preceding scan) for the band of coefficients specified by Ss and Se. This parameter shall be set to zero for the first scan of each band of coefficients. In the lossless mode of operations this parameter has no meaning. It shall be set to zero.",
1134 { "Successive approximation bit position low or point transform",
1136 FT_UINT8, BASE_DEC, NULL, 0x0F,
1137 "In the DCT modes of operation this parameter specifies the point transform, i.e. bit position low, used before coding the band of coefficients specified by Ss and Se. This parameter shall be set to zero for the sequential DCT processes. In the lossless mode of operations, this parameter specifies the point transform, Pt.",
1143 /* Setup protocol subtree array */
1144 static gint *ett[] = {
1146 &ett_marker_segment,
1150 /* Register the protocol name and description */
1151 proto_jfif = proto_register_protocol(
1152 "JPEG File Interchange Format",
1153 "JFIF (JPEG) image",
1157 /* Required function calls to register the header fields
1158 * and subtrees used */
1159 proto_register_field_array(proto_jfif, hf, array_length(hf));
1160 proto_register_subtree_array(ett, array_length(ett));
1162 new_register_dissector(IMG_JFIF, dissect_jfif, proto_jfif);
1167 proto_reg_handoff_jfif(void)
1169 dissector_handle_t jfif_handle = find_dissector(IMG_JFIF);
1171 /* Register the JPEG media type */
1172 dissector_add_string("media_type", "image/jfif", jfif_handle);
1173 dissector_add_string("media_type", "image/jpg", jfif_handle);
1174 dissector_add_string("media_type", "image/jpeg", jfif_handle);
1176 dissector_add_uint("wtap_encap", WTAP_ENCAP_JPEG_JFIF, jfif_handle);
1178 heur_dissector_add("http", dissect_jfif_heur, proto_jfif);