Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / file-gif.c
blobb5e37a50d3d6e668b73082587faf8034093c6b63
1 /* file-gif.c
3 * Routines for image/gif media dissection
4 * Copyright 2003, 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
13 * Compuserve GIF media decoding functionality provided by Olivier Biot.
15 * The two GIF specifications are found at several locations, such as W3C:
16 * http://www.w3.org/Graphics/GIF/spec-gif87.txt
17 * http://www.w3.org/Graphics/GIF/spec-gif89a.txt
19 * SPDX-License-Identifier: GPL-2.0-or-later
21 #include "config.h"
23 #include <epan/packet.h>
24 #include <epan/expert.h>
25 #include <wsutil/array.h>
27 #include <wsutil/str_util.h>
29 void proto_register_gif(void);
30 void proto_reg_handoff_gif(void);
33 /************************** Variable declarations **************************/
35 static const value_string vals_true_false[] = {
36 { 0, "False" },
37 { 1, "True" },
38 { 0, NULL },
41 static const value_string vals_extensions[] = {
42 { 0xF9, "Graphics Control" },
43 { 0xFE, "Comment" },
44 { 0xFF, "Application" },
45 { 0x01, "Plain Text" },
46 { 0x00, NULL },
49 enum {
50 GIF_87a = 0x87,
51 GIF_89a = 0x89
54 static dissector_handle_t gif_handle;
56 static int proto_gif;
58 static int hf_background_color;
59 static int hf_data_block;
60 static int hf_extension;
61 static int hf_extension_label;
62 static int hf_global_color_map;
63 static int hf_global_color_map_ordered;
64 static int hf_global_color_map_present;
65 static int hf_global_color_resolution;
66 static int hf_global_image_bpp;
67 static int hf_image;
68 static int hf_image_code_size;
69 static int hf_image_height;
70 static int hf_image_left;
71 static int hf_image_top;
72 static int hf_image_width;
73 static int hf_local_color_map;
74 static int hf_local_color_map_ordered;
75 static int hf_local_color_map_present;
76 static int hf_local_color_resolution;
77 static int hf_local_image_bpp;
78 static int hf_pixel_aspect_ratio;
79 static int hf_screen_height;
80 static int hf_screen_width;
81 static int hf_trailer;
82 static int hf_version;
84 /* Initialize the subtree pointers */
85 static int ett_gif;
86 static int ett_global_flags;
87 static int ett_local_flags;
88 static int ett_extension;
89 static int ett_image;
91 static expert_field ei_gif_unknown_data_block_type;
93 /****************** GIF protocol dissection functions ******************/
95 static int
96 dissect_gif_data_block_seq(tvbuff_t *tvb, int offset, proto_tree *tree)
98 int offset_start = offset;
99 uint8_t len;
100 proto_item *db_ti;
102 do {
103 /* Read length of data block */
104 len = tvb_get_uint8(tvb, offset);
105 db_ti = proto_tree_add_item(tree, hf_data_block,
106 tvb, offset, 1, ENC_NA);
107 proto_item_append_text(db_ti, " (length = %u)", len);
108 offset += (1 + len);
109 } while (len > 0);
111 return offset - offset_start;
114 /* There are two Compuserve GIF standards: GIF87a and GIF89a. GIF image files
115 * always convey their version in the first 6 bytes, written as an US-ASCII
116 * string representation of the version: "GIF87a" or "GIF89a".
119 static int
120 dissect_gif(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
122 proto_item *ti;
123 proto_tree *gif_tree; /* Main GIF tree */
124 proto_tree *subtree;
125 unsigned offset = 0;
126 uint8_t peek;
127 bool color_map_present;
128 uint8_t color_resolution;
129 uint8_t image_bpp;
130 const uint8_t *ver_str;
131 uint8_t version;
133 if (tvb_reported_length(tvb) < 20)
134 return 0;
136 /* Check whether we're processing a GIF object */
137 /* see http://www.w3.org/Graphics/GIF/spec-gif89a.txt section 17 */
138 if (tvb_strneql(tvb, 0, "GIF87a", 6) == 0) {
139 version = GIF_87a;
140 } else if (tvb_strneql(tvb, 0, "GIF89a", 6) == 0) {
141 version = GIF_89a;
142 } else {
143 /* Not a GIF image! */
144 return 0;
147 ti = proto_tree_add_item(tree, proto_gif, tvb, offset, -1, ENC_NA);
148 gif_tree = proto_item_add_subtree(ti, ett_gif);
150 /* GIF signature */
151 proto_tree_add_item_ret_string(gif_tree, hf_version,
152 tvb, offset, 6, ENC_ASCII|ENC_NA, pinfo->pool, &ver_str);
153 proto_item_append_text(ti, ", Version: %s", ver_str);
154 col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", ver_str);
155 offset += 6;
157 /* Screen descriptor */
158 proto_tree_add_item(gif_tree, hf_screen_width, tvb, offset, 2, ENC_LITTLE_ENDIAN);
159 offset += 2;
160 proto_tree_add_item(gif_tree, hf_screen_height, tvb, offset, 2, ENC_LITTLE_ENDIAN);
161 offset += 2;
163 peek = tvb_get_uint8(tvb, offset);
164 /* Bitfield gccc 0ppp
165 * g... .... : global color map present
166 * .ccc .... : color resolution in bits (add one)
167 * .... 0... : GIF87a - reserved (no use)
168 * GIF89a - ordered (most important color 1st)
169 * .... .ppp : bits per pixel in image (add one)
171 color_map_present = peek & 0x80;
172 color_resolution = 1 + ((peek & 0x60) >> 4);
173 image_bpp = 1 + (peek & 0x07);
175 subtree = proto_tree_add_subtree(gif_tree, tvb, offset, 1, ett_global_flags, &ti,
176 "Global settings:");
177 if (color_map_present)
178 proto_item_append_text(ti, " (Global color table present)");
179 proto_item_append_text(ti,
180 " (%u bit%s per color) (%u bit%s per pixel)",
181 color_resolution, plurality(color_resolution, "", "s"),
182 image_bpp, plurality(image_bpp, "", "s"));
183 proto_tree_add_item(subtree, hf_global_color_map_present,
184 tvb, offset, 1, ENC_LITTLE_ENDIAN);
185 proto_tree_add_item(subtree, hf_global_color_resolution,
186 tvb, offset, 1, ENC_LITTLE_ENDIAN);
187 if (version == GIF_89a) {
188 proto_tree_add_item(subtree, hf_global_color_map_ordered,
189 tvb, offset, 1, ENC_LITTLE_ENDIAN);
191 proto_tree_add_item(subtree, hf_global_image_bpp,
192 tvb, offset, 1, ENC_LITTLE_ENDIAN);
193 offset++;
195 /* Background color */
196 proto_tree_add_item(gif_tree, hf_background_color,
197 tvb, offset, 1, ENC_LITTLE_ENDIAN);
198 offset++;
200 /* byte at offset 12 is 0x00 - reserved in GIF87a but encodes the
201 * pixel aspect ratio in GIF89a as:
202 * aspect-ratio = (15 + pixel-aspect-ratio) / 64
203 * where the aspect-ratio is not computed if pixel-aspect-ratio == 0
205 if (version == GIF_89a) {
206 peek = tvb_get_uint8(tvb, offset);
207 if (peek) {
208 /* Only display if different from 0 */
209 proto_tree_add_uint_format(gif_tree, hf_pixel_aspect_ratio,
210 tvb, offset, 1, peek,
211 "%u, yields an aspect ratio of (15 + %u) / 64 = %.2f",
212 peek, peek, (float)(15 + peek) / 64.0);
215 offset++;
217 /* Global color map
218 * If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B)
219 * that contain the Red, Green and Blue intensity of the colors
220 * in the Global Color Map */
221 if (color_map_present) {
222 unsigned len;
224 len = 3 * (1 << image_bpp);
225 proto_tree_add_item(gif_tree, hf_global_color_map,
226 tvb, offset, len, ENC_NA);
227 offset += len;
231 /* From now on, a set of images prefixed with the image separator
232 * character 0x2C (',') will appear in the byte stream. Each image
233 * hence consists of:
234 * - The image separator character 0x2C
235 * - Image left (16 bits LSB first): pixels from left border
236 * - Image top (16 bits LSB first): pixels from to border
237 * - Image width (16 bits LSB first)
238 * - Image height (16 bits LSB first)
239 * - A bitfield MI00 0ppp
240 * M... .... : Use global color map if unset (ignore ppp);
241 * if set a local color map will be defined.
242 * .I.. .... : Image formatted in interlaced order if set;
243 * otherwise it is plain sequential order
244 * ..0. .... : GIF87a - Reserved
245 * ..s. .... GIF89a - Set if local color map is ordered
246 * ...0 0... : Reserved
247 * .... .ppp : bits per pixel in image (add one)
248 * - If the local color map bit is set, then a local color table follows
249 * with length = 3 x 2 ^ (1 + bits per pixel)
250 * - The raster data
252 * NOTE that the GIF specification only requires that:
253 * image left + image width <= screen width
254 * image top + image height <= screen height
256 * The Raster Data is encoded as follows:
257 * - Code size (1 byte)
258 * - Blocks consisting of
259 * o Byte count (1 byte): number of bytes in the block
260 * o Data bytes: as many as specified in the byte count
261 * End of data is given with an empty block (byte count == 0).
264 * GIF terminator
265 * This is a synchronization method, based on the final character 0x3B
266 * (';') at the end of an image
269 * GIF extension
270 * This is a block of data encoded as:
271 * - The GIF extension block introducer 0x21 ('!')
272 * - The extension function code (1 byte)
273 * - Blocks consisting of
274 * o Byte count (1 byte): number of bytes in the block
275 * o Data bytes: as many as specified in the byte count
276 * End of data is given with an empty block (byte count == 0).
278 * NOTE that the GIF extension block can only appear at the following
279 * locations:
280 * - Immediately before an Image Descriptor
281 * - Before the GIF termination character
283 while (tvb_reported_length_remaining(tvb, offset)) {
284 int ret;
285 int offset_start = offset;
287 peek = tvb_get_uint8(tvb, offset);
288 if (peek == 0x21) { /* GIF extension block */
289 ti = proto_tree_add_item(gif_tree, hf_extension,
290 tvb, offset, 1, ENC_NA);
291 subtree = proto_item_add_subtree(ti, ett_extension);
292 offset++;
293 proto_tree_add_item(subtree, hf_extension_label,
294 tvb, offset, 1, ENC_LITTLE_ENDIAN);
295 peek = tvb_get_uint8(tvb, offset);
296 proto_item_append_text(ti, ": %s",
297 val_to_str(peek, vals_extensions,
298 "<Warning: Unknown extension 0x%02X>"));
299 offset++;
300 ret = dissect_gif_data_block_seq(tvb, offset, subtree);
301 if (ret <= 0)
302 break;
303 offset += ret;
304 } else if (peek == 0x2C) { /* Image separator */
305 proto_tree *subtree2;
306 proto_item *ti2;
308 ti = proto_tree_add_item(gif_tree, hf_image,
309 tvb, offset, 1, ENC_NA);
310 subtree = proto_item_add_subtree(ti, ett_image);
311 offset++;
312 /* Screen descriptor */
313 proto_tree_add_item(subtree, hf_image_left,
314 tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
315 proto_tree_add_item(subtree, hf_image_top,
316 tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
317 proto_tree_add_item(subtree, hf_image_width,
318 tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
319 proto_tree_add_item(subtree, hf_image_height,
320 tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
321 /* bit field */
322 peek = tvb_get_uint8(tvb, offset);
323 color_map_present = peek & 0x80;
324 color_resolution = 1 + ((peek & 0x60) >> 4);
325 image_bpp = 1 + (peek & 0x07);
327 subtree2 = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_local_flags, &ti2,
328 "Local settings:");
329 if (color_map_present)
330 proto_item_append_text(ti2, " (Local color table present)");
331 proto_item_append_text(ti2,
332 " (%u bit%s per color) (%u bit%s per pixel)",
333 color_resolution, plurality(color_resolution, "", "s"),
334 image_bpp, plurality(image_bpp, "", "s"));
335 proto_tree_add_item(subtree2, hf_local_color_map_present,
336 tvb, offset, 1, ENC_LITTLE_ENDIAN);
337 proto_tree_add_item(subtree2, hf_local_color_resolution,
338 tvb, offset, 1, ENC_LITTLE_ENDIAN);
339 if (version == GIF_89a) {
340 proto_tree_add_item(subtree2, hf_local_color_map_ordered,
341 tvb, offset, 1, ENC_LITTLE_ENDIAN);
343 proto_tree_add_item(subtree2, hf_global_image_bpp,
344 tvb, offset, 1, ENC_LITTLE_ENDIAN);
345 offset++;
347 /* Local color map
348 * If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B)
349 * that contain the Red, Green and Blue intensity of the colors
350 * in the Local Color Map */
351 if (color_map_present) {
352 unsigned len;
354 len = 3 * (1 << image_bpp);
355 proto_tree_add_item(subtree, hf_local_color_map,
356 tvb, offset, len, ENC_NA);
357 offset += len;
360 proto_tree_add_item(subtree, hf_image_code_size,
361 tvb, offset, 1, ENC_LITTLE_ENDIAN);
362 offset++;
363 ret = dissect_gif_data_block_seq(tvb, offset, subtree);
364 if (ret <= 0)
365 break;
366 offset += ret;
367 } else if (peek == 0x3B) { /* Trailer byte */
368 /* GIF processing stops at this very byte */
369 proto_tree_add_item(gif_tree, hf_trailer,
370 tvb, offset, 1, ENC_NA);
371 offset++;
372 break;
373 } else {
374 proto_tree_add_expert(gif_tree, pinfo,
375 &ei_gif_unknown_data_block_type,
376 tvb, offset, 1);
377 offset++;
379 proto_item_set_len(ti, offset-offset_start);
380 } /* while */
382 return offset;
385 static bool
386 dissect_gif_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
388 return dissect_gif(tvb, pinfo, tree, data) > 0;
392 /****************** Register the protocol with Wireshark ******************/
395 /* This format is required because a script is used to build the C function
396 * that calls the protocol registration. */
398 void
399 proto_register_gif(void)
401 static hf_register_info hf[] = {
402 { &hf_version,
403 { "Version", "image-gif.version",
404 FT_STRING, BASE_NONE, NULL, 0x00,
405 "GIF Version", HFILL }
407 { &hf_screen_width,
408 { "Screen width", "image-gif.screen.width",
409 FT_UINT16, BASE_DEC, NULL, 0x00,
410 NULL, HFILL }
412 { &hf_screen_height,
413 { "Screen height", "image-gif.screen.height",
414 FT_UINT16, BASE_DEC, NULL, 0x00,
415 NULL, HFILL }
417 { &hf_global_color_map_present,
418 { "Global color map is present", "image-gif.global.color_map.present",
419 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80,
420 "Indicates if the global color map is present", HFILL }
422 { &hf_global_color_resolution,
423 { "Bits per color minus 1", "image-gif.global.color_bpp",
424 FT_UINT8, BASE_DEC, NULL, 0x70,
425 "The number of bits per color is one plus the field value.", HFILL }
427 { &hf_global_color_map_ordered,
428 { "Global color map is ordered", "image-gif.global.color_map.ordered",
429 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08,
430 "Indicates whether the global color map is ordered.", HFILL }
432 { &hf_global_image_bpp,
433 { "Image bits per pixel minus 1", "image-gif.global.bpp",
434 FT_UINT8, BASE_DEC, NULL, 0x07,
435 "The number of bits per pixel is one plus the field value.", HFILL }
437 { &hf_background_color,
438 { "Background color index", "image-gif.image_background_index",
439 FT_UINT8, BASE_DEC, NULL, 0x00,
440 "Index of the background color in the color map.", HFILL }
442 { &hf_pixel_aspect_ratio,
443 { "Global pixel aspect ratio", "image-gif.global.pixel_aspect_ratio",
444 FT_UINT8, BASE_DEC, NULL, 0x00,
445 "Gives an approximate value of the aspect ratio of the pixels.", HFILL }
447 { &hf_global_color_map,
448 { "Global color map", "image-gif.global.color_map",
449 FT_BYTES, BASE_NONE, NULL, 0x00,
450 "Global color map.", HFILL }
452 { &hf_image_left,
453 { "Image left position", "image-gif.image.left",
454 FT_UINT16, BASE_DEC, NULL, 0x00,
455 "Offset between left of Screen and left of Image.", HFILL }
457 { &hf_image_top,
458 { "Image top position", "image-gif.image.top",
459 FT_UINT16, BASE_DEC, NULL, 0x00,
460 "Offset between top of Screen and top of Image.", HFILL }
462 { &hf_image_width,
463 { "Image width", "image-gif.image.width",
464 FT_UINT16, BASE_DEC, NULL, 0x00,
465 "Image width.", HFILL }
467 { &hf_image_height,
468 { "Image height", "image-gif.image.height",
469 FT_UINT16, BASE_DEC, NULL, 0x00,
470 "Image height.", HFILL }
472 { &hf_local_color_map_present,
473 { "Local color map is present", "image-gif.local.color_map.present",
474 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80,
475 "Indicates if the local color map is present", HFILL }
477 { &hf_local_color_resolution,
478 { "Bits per color minus 1", "image-gif.local.color_bpp",
479 FT_UINT8, BASE_DEC, NULL, 0x70,
480 "The number of bits per color is one plus the field value.", HFILL }
482 { &hf_local_color_map_ordered,
483 { "Local color map is ordered", "image-gif.local.color_map.ordered",
484 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08,
485 "Indicates whether the local color map is ordered.", HFILL }
487 { &hf_local_image_bpp,
488 { "Image bits per pixel minus 1", "image-gif.local.bpp",
489 FT_UINT8, BASE_DEC, NULL, 0x07,
490 "The number of bits per pixel is one plus the field value.", HFILL }
492 { &hf_local_color_map,
493 { "Local color map", "image-gif.local.color_map",
494 FT_BYTES, BASE_NONE, NULL, 0x00,
495 "Local color map.", HFILL }
497 { &hf_extension,
498 { "Extension", "image-gif.extension",
499 FT_NONE, BASE_NONE, NULL, 0x00,
500 "Extension.", HFILL }
502 { &hf_extension_label,
503 { "Extension label", "image-gif.extension.label",
504 FT_UINT8, BASE_HEX, VALS(vals_extensions), 0x00,
505 "Extension label.", HFILL }
507 { &hf_image,
508 { "Image", "image-gif.image",
509 FT_NONE, BASE_NONE, NULL, 0x00,
510 "Image.", HFILL }
512 { &hf_image_code_size,
513 { "LZW minimum code size", "image-gif.image.code_size",
514 FT_UINT8, BASE_DEC, NULL, 0x00,
515 "Minimum code size for the LZW compression.", HFILL }
517 { &hf_trailer,
518 { "Trailer (End of the GIF stream)", "image-gif.end",
519 FT_NONE, BASE_NONE, NULL, 0x00,
520 "This byte tells the decoder that the data stream is finished.", HFILL }
522 { &hf_data_block,
523 { "Data block", "image-gif.data_block",
524 FT_UINT_BYTES, BASE_NONE|BASE_ALLOW_ZERO, NULL, 0x00,
525 NULL, HFILL }
529 /* Setup protocol subtree array */
530 static int *ett[] = {
531 &ett_gif,
532 &ett_global_flags,
533 &ett_local_flags,
534 &ett_extension,
535 &ett_image,
538 static ei_register_info ei[] = {
539 { &ei_gif_unknown_data_block_type,
540 { "gif.data_block_type.unknown", PI_PROTOCOL, PI_WARN,
541 "Unknown GIF data block type", EXPFILL }
545 expert_module_t* expert_gif;
547 /* Register the protocol name and description */
548 proto_gif = proto_register_protocol(
549 "Compuserve GIF",
550 "GIF image",
551 "image-gif"
554 /* Required function calls to register the header fields
555 * and subtrees used */
556 proto_register_field_array(proto_gif, hf, array_length(hf));
557 proto_register_subtree_array(ett, array_length(ett));
558 expert_gif = expert_register_protocol(proto_gif);
559 expert_register_field_array(expert_gif, ei, array_length(ei));
561 gif_handle = register_dissector("image-gif", dissect_gif, proto_gif);
565 void
566 proto_reg_handoff_gif(void)
568 /* Register the GIF media type */
569 dissector_add_string("media_type", "image/gif", gif_handle);
570 heur_dissector_add("http", dissect_gif_heur, "GIF file in HTTP", "gif_http", proto_gif, HEURISTIC_ENABLE);
571 heur_dissector_add("wtap_file", dissect_gif_heur, "GIF file", "gif_wtap", proto_gif, HEURISTIC_ENABLE);
575 * Editor modelines - https://www.wireshark.org/tools/modelines.html
577 * Local variables:
578 * c-basic-offset: 4
579 * tab-width: 8
580 * indent-tabs-mode: nil
581 * End:
583 * vi: set shiftwidth=4 tabstop=8 expandtab:
584 * :indentSize=4:tabSize=8:noTabs=true: