2014-01-24 14:45:43 +00:00
|
|
|
/* file-gif.c
|
2003-12-24 02:06:26 +00:00
|
|
|
*
|
|
|
|
* Routines for image/gif media dissection
|
2004-03-08 22:03:59 +00:00
|
|
|
* Copyright 2003, 2004, Olivier Biot.
|
2003-12-24 02:06:26 +00:00
|
|
|
*
|
2004-03-08 22:03:59 +00:00
|
|
|
* Refer to the AUTHORS file or the AUTHORS section in the man page
|
|
|
|
* for contacting the author(s) of this file.
|
2003-12-24 02:06:26 +00:00
|
|
|
*
|
2006-05-21 04:49:01 +00:00
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
2003-12-24 02:06:26 +00:00
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* Compuserve GIF media decoding functionality provided by Olivier Biot.
|
2011-03-24 02:49:05 +00:00
|
|
|
*
|
2003-12-24 02:06:26 +00:00
|
|
|
* The two GIF specifications are found at several locations, such as W3C:
|
|
|
|
* http://www.w3.org/Graphics/GIF/spec-gif87.txt
|
|
|
|
* http://www.w3.org/Graphics/GIF/spec-gif89a.txt
|
2011-03-24 02:49:05 +00:00
|
|
|
*
|
2003-12-24 02:06:26 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2011-03-24 02:49:05 +00:00
|
|
|
*
|
2003-12-24 02:06:26 +00:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2011-03-24 02:49:05 +00:00
|
|
|
*
|
2003-12-24 02:06:26 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2012-06-28 23:18:38 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2003-12-24 02:06:26 +00:00
|
|
|
*/
|
|
|
|
|
2013-08-30 21:31:42 +00:00
|
|
|
#define NEW_PROTO_TREE_API
|
|
|
|
|
2012-09-20 02:03:38 +00:00
|
|
|
#include "config.h"
|
2003-12-24 02:06:26 +00:00
|
|
|
|
|
|
|
#include <epan/packet.h>
|
2017-05-28 08:26:05 +00:00
|
|
|
#include <epan/expert.h>
|
2003-12-24 02:06:26 +00:00
|
|
|
|
2015-11-04 08:45:54 +00:00
|
|
|
#include <wsutil/str_util.h>
|
|
|
|
|
2013-12-14 12:45:57 +00:00
|
|
|
void proto_register_gif(void);
|
|
|
|
void proto_reg_handoff_gif(void);
|
|
|
|
|
2003-12-24 02:06:26 +00:00
|
|
|
#define IMG_GIF "image-gif"
|
|
|
|
|
|
|
|
/************************** Variable declarations **************************/
|
|
|
|
|
|
|
|
static const value_string vals_true_false[] = {
|
2012-05-15 19:23:35 +00:00
|
|
|
{ 0, "False" },
|
|
|
|
{ 1, "True" },
|
|
|
|
{ 0, NULL },
|
2003-12-24 02:06:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const value_string vals_extensions[] = {
|
2012-05-15 19:23:35 +00:00
|
|
|
{ 0xF9, "Graphics Control" },
|
|
|
|
{ 0xFE, "Comment" },
|
|
|
|
{ 0xFF, "Application" },
|
|
|
|
{ 0x01, "Plain Text" },
|
|
|
|
{ 0x00, NULL },
|
2003-12-24 02:06:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
2012-05-15 19:23:35 +00:00
|
|
|
GIF_87a = 0x87,
|
|
|
|
GIF_89a = 0x89
|
2003-12-24 02:06:26 +00:00
|
|
|
};
|
|
|
|
|
2013-08-30 21:31:42 +00:00
|
|
|
static dissector_handle_t gif_handle;
|
|
|
|
|
|
|
|
/* Protocol and registered fields */
|
|
|
|
static header_field_info *hfi_gif = NULL;
|
|
|
|
|
|
|
|
#define GIF_HFI_INIT HFI_INIT(proto_gif)
|
2003-12-24 02:06:26 +00:00
|
|
|
|
|
|
|
/* header fields */
|
|
|
|
/* GIF signature */
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_version GIF_HFI_INIT = {
|
|
|
|
"Version",
|
|
|
|
IMG_GIF ".version",
|
|
|
|
FT_STRING, BASE_NONE, NULL, 0x00,
|
|
|
|
"GIF Version",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2003-12-24 02:06:26 +00:00
|
|
|
/* Screen descriptor */
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_screen_width GIF_HFI_INIT = {
|
|
|
|
"Screen width",
|
|
|
|
IMG_GIF ".screen.width",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0x00,
|
|
|
|
NULL,
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_screen_height GIF_HFI_INIT = {
|
|
|
|
"Screen height",
|
|
|
|
IMG_GIF ".screen.height",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0x00,
|
|
|
|
NULL,
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_global_color_map_present GIF_HFI_INIT = {
|
|
|
|
"Global color map is present",
|
|
|
|
IMG_GIF ".global.color_map.present",
|
|
|
|
FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80,
|
|
|
|
"Indicates if the global color map is present",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_global_color_resolution GIF_HFI_INIT = {
|
|
|
|
"Bits per color minus 1",
|
|
|
|
IMG_GIF ".global.color_bpp",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x70,
|
|
|
|
"The number of bits per color is one plus the field value.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_global_color_map_ordered/* GIF89a */ GIF_HFI_INIT = {
|
|
|
|
"Global color map is ordered",
|
|
|
|
IMG_GIF ".global.color_map.ordered",
|
|
|
|
FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08,
|
|
|
|
"Indicates whether the global color map is ordered.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_global_image_bpp GIF_HFI_INIT = {
|
|
|
|
"Image bits per pixel minus 1",
|
|
|
|
IMG_GIF ".global.bpp",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x07,
|
|
|
|
"The number of bits per pixel is one plus the field value.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2003-12-24 02:06:26 +00:00
|
|
|
/* Only makes sense if the global color map is present: */
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_background_color GIF_HFI_INIT = {
|
|
|
|
"Background color index",
|
|
|
|
IMG_GIF ".image_background_index",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x00,
|
|
|
|
"Index of the background color in the color map.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_pixel_aspect_ratio/* GIF89a */ GIF_HFI_INIT = {
|
|
|
|
"Global pixel aspect ratio",
|
|
|
|
IMG_GIF ".global.pixel_aspect_ratio",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x00,
|
|
|
|
"Gives an approximate value of the aspect ratio of the pixels.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_global_color_map GIF_HFI_INIT = {
|
|
|
|
"Global color map",
|
|
|
|
IMG_GIF ".global.color_map",
|
|
|
|
FT_BYTES, BASE_NONE, NULL, 0x00,
|
|
|
|
"Global color map.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2003-12-24 02:06:26 +00:00
|
|
|
|
|
|
|
/* Image descriptor */
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_image_left GIF_HFI_INIT = {
|
|
|
|
"Image left position",
|
|
|
|
IMG_GIF ".image.left",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0x00,
|
|
|
|
"Offset between left of Screen and left of Image.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_image_top GIF_HFI_INIT = {
|
|
|
|
"Image top position",
|
|
|
|
IMG_GIF ".image.top",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0x00,
|
|
|
|
"Offset between top of Screen and top of Image.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_image_width GIF_HFI_INIT = {
|
|
|
|
"Image width",
|
|
|
|
IMG_GIF ".image.width",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0x00,
|
|
|
|
"Image width.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_image_height GIF_HFI_INIT = {
|
|
|
|
"Image height",
|
|
|
|
IMG_GIF ".image.height",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0x00,
|
|
|
|
"Image height.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_local_color_map_present GIF_HFI_INIT = {
|
|
|
|
"Local color map is present",
|
|
|
|
IMG_GIF ".local.color_map.present",
|
|
|
|
FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80,
|
|
|
|
"Indicates if the local color map is present",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_local_color_resolution GIF_HFI_INIT = {
|
|
|
|
"Bits per color minus 1",
|
|
|
|
IMG_GIF ".local.color_bpp",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x70,
|
|
|
|
"The number of bits per color is one plus the field value.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_local_color_map_ordered/* GIF89a */ GIF_HFI_INIT = {
|
|
|
|
"Local color map is ordered",
|
|
|
|
IMG_GIF ".local.color_map.ordered",
|
|
|
|
FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08,
|
|
|
|
"Indicates whether the local color map is ordered.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
|
|
|
#if 0
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_local_image_bpp GIF_HFI_INIT = {
|
|
|
|
"Image bits per pixel minus 1",
|
|
|
|
IMG_GIF ".local.bpp",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x07,
|
|
|
|
"The number of bits per pixel is one plus the field value.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
#endif
|
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_local_color_map GIF_HFI_INIT = {
|
|
|
|
"Local color map",
|
|
|
|
IMG_GIF ".local.color_map",
|
|
|
|
FT_BYTES, BASE_NONE, NULL, 0x00,
|
|
|
|
"Local color map.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
|
|
|
/* Extension */
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_extension GIF_HFI_INIT = {
|
|
|
|
"Extension",
|
|
|
|
IMG_GIF ".extension",
|
|
|
|
FT_NONE, BASE_NONE, NULL, 0x00,
|
|
|
|
"Extension.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_extension_label GIF_HFI_INIT = {
|
|
|
|
"Extension label",
|
|
|
|
IMG_GIF ".extension.label",
|
|
|
|
FT_UINT8, BASE_HEX, VALS(vals_extensions), 0x00,
|
|
|
|
"Extension label.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_image GIF_HFI_INIT = {
|
|
|
|
"Image",
|
|
|
|
IMG_GIF ".image",
|
|
|
|
FT_NONE, BASE_NONE, NULL, 0x00,
|
|
|
|
"Image.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_image_code_size GIF_HFI_INIT = {
|
|
|
|
"LZW minimum code size",
|
|
|
|
IMG_GIF ".image.code_size",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0x00,
|
|
|
|
"Minimum code size for the LZW compression.",
|
|
|
|
HFILL
|
|
|
|
};
|
2003-12-24 02:06:26 +00:00
|
|
|
|
|
|
|
/* Trailer (end of GIF data stream) */
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_trailer GIF_HFI_INIT = {
|
|
|
|
"Trailer (End of the GIF stream)",
|
|
|
|
IMG_GIF ".end",
|
|
|
|
FT_NONE, BASE_NONE, NULL, 0x00,
|
|
|
|
"This byte tells the decoder that the data stream is finished.",
|
|
|
|
HFILL
|
|
|
|
};
|
2013-08-30 21:31:42 +00:00
|
|
|
|
2014-10-05 19:54:49 +00:00
|
|
|
static header_field_info hfi_data_block GIF_HFI_INIT = {
|
|
|
|
"Data block",
|
|
|
|
IMG_GIF ".data_block",
|
2017-05-08 15:47:46 +00:00
|
|
|
FT_UINT_BYTES, BASE_NONE|BASE_ALLOW_ZERO, NULL, 0x00,
|
2014-10-05 19:54:49 +00:00
|
|
|
NULL, HFILL };
|
2014-09-26 23:28:01 +00:00
|
|
|
|
2003-12-24 02:06:26 +00:00
|
|
|
|
|
|
|
/* Initialize the subtree pointers */
|
|
|
|
static gint ett_gif = -1;
|
|
|
|
static gint ett_global_flags = -1;
|
|
|
|
static gint ett_local_flags = -1;
|
|
|
|
static gint ett_extension = -1;
|
|
|
|
static gint ett_image = -1;
|
|
|
|
|
2017-05-28 08:26:05 +00:00
|
|
|
static expert_field ei_gif_unknown_data_block_type = EI_INIT;
|
2003-12-24 02:06:26 +00:00
|
|
|
|
|
|
|
/****************** GIF protocol dissection functions ******************/
|
|
|
|
|
2017-05-07 12:05:53 +00:00
|
|
|
static gint
|
|
|
|
dissect_gif_data_block_seq(tvbuff_t *tvb, gint offset, proto_tree *tree)
|
|
|
|
{
|
|
|
|
gint offset_start = offset;
|
|
|
|
guint8 len;
|
|
|
|
proto_item *db_ti;
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* Read length of data block */
|
|
|
|
len = tvb_get_guint8(tvb, offset);
|
|
|
|
db_ti = proto_tree_add_item(tree, &hfi_data_block,
|
|
|
|
tvb, offset, 1, ENC_NA);
|
|
|
|
proto_item_append_text(db_ti, " (length = %u)", len);
|
|
|
|
offset += (1 + len);
|
|
|
|
} while (len > 0);
|
|
|
|
|
|
|
|
return offset - offset_start;
|
|
|
|
}
|
|
|
|
|
2003-12-24 02:06:26 +00:00
|
|
|
/* There are two Compuserve GIF standards: GIF87a and GIF89a. GIF image files
|
|
|
|
* always convey their version in the first 6 bytes, written as an US-ASCII
|
|
|
|
* string representation of the version: "GIF87a" or "GIF89a".
|
|
|
|
*/
|
|
|
|
|
2011-03-24 02:49:05 +00:00
|
|
|
static gint
|
2012-09-10 21:40:21 +00:00
|
|
|
dissect_gif(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
2003-12-24 02:06:26 +00:00
|
|
|
{
|
2012-05-15 19:23:35 +00:00
|
|
|
proto_item *ti;
|
|
|
|
proto_tree *gif_tree; /* Main GIF tree */
|
2017-05-07 11:53:22 +00:00
|
|
|
proto_tree *subtree;
|
2012-05-15 19:23:35 +00:00
|
|
|
guint offset = 0, len = 0;
|
|
|
|
guint8 peek;
|
|
|
|
gboolean color_map_present;
|
|
|
|
guint8 color_resolution;
|
|
|
|
guint8 image_bpp;
|
2017-05-06 17:37:07 +00:00
|
|
|
const guint8 *ver_str;
|
2017-05-06 17:34:24 +00:00
|
|
|
guint8 version;
|
2012-05-15 19:23:35 +00:00
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
if (tvb_reported_length(tvb) < 20)
|
2012-05-15 19:23:35 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Check whether we're processing a GIF object */
|
|
|
|
/* see http://www.w3.org/Graphics/GIF/spec-gif89a.txt section 17 */
|
2017-05-07 20:59:08 +00:00
|
|
|
if (tvb_strneql(tvb, 0, "GIF87a", 6) == 0) {
|
2012-05-15 19:23:35 +00:00
|
|
|
version = GIF_87a;
|
2017-05-07 20:59:08 +00:00
|
|
|
} else if (tvb_strneql(tvb, 0, "GIF89a", 6) == 0) {
|
2012-05-15 19:23:35 +00:00
|
|
|
version = GIF_89a;
|
|
|
|
} else {
|
|
|
|
/* Not a GIF image! */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-07 12:05:53 +00:00
|
|
|
ti = proto_tree_add_item(tree, hfi_gif, tvb, offset, -1, ENC_NA);
|
2017-05-06 17:37:07 +00:00
|
|
|
gif_tree = proto_item_add_subtree(ti, ett_gif);
|
2017-05-07 12:05:53 +00:00
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
/* GIF signature */
|
|
|
|
proto_tree_add_item_ret_string(gif_tree, hfi_version.id,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, 6, ENC_ASCII|ENC_NA, wmem_packet_scope(), &ver_str);
|
2017-05-06 17:37:07 +00:00
|
|
|
proto_item_append_text(ti, ", Version: %s", ver_str);
|
2017-05-06 17:34:24 +00:00
|
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", ver_str);
|
2017-05-07 12:05:53 +00:00
|
|
|
offset += 6;
|
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
/* Screen descriptor */
|
2017-05-07 12:05:53 +00:00
|
|
|
proto_tree_add_item(gif_tree, &hfi_screen_width, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
|
|
offset += 2;
|
|
|
|
proto_tree_add_item(gif_tree, &hfi_screen_height, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
|
|
|
offset += 2;
|
2017-05-06 17:37:07 +00:00
|
|
|
|
2017-05-07 12:05:53 +00:00
|
|
|
peek = tvb_get_guint8(tvb, offset);
|
2017-05-06 17:37:07 +00:00
|
|
|
/* Bitfield gccc 0ppp
|
|
|
|
* g... .... : global color map present
|
|
|
|
* .ccc .... : color resolution in bits (add one)
|
|
|
|
* .... 0... : GIF87a - reserved (no use)
|
|
|
|
* GIF89a - ordered (most important color 1st)
|
|
|
|
* .... .ppp : bits per pixel in image (add one)
|
2012-05-15 19:23:35 +00:00
|
|
|
*/
|
2017-05-06 17:37:07 +00:00
|
|
|
color_map_present = peek & 0x80;
|
|
|
|
color_resolution = 1 + ((peek & 0x60) >> 4);
|
|
|
|
image_bpp = 1 + (peek & 0x07);
|
|
|
|
|
2017-05-07 12:05:53 +00:00
|
|
|
subtree = proto_tree_add_subtree(gif_tree, tvb, offset, 1, ett_global_flags, &ti,
|
2017-05-06 17:37:07 +00:00
|
|
|
"Global settings:");
|
|
|
|
if (color_map_present)
|
|
|
|
proto_item_append_text(ti, " (Global color table present)");
|
|
|
|
proto_item_append_text(ti,
|
|
|
|
" (%u bit%s per color) (%u bit%s per pixel)",
|
|
|
|
color_resolution, plurality(color_resolution, "", "s"),
|
|
|
|
image_bpp, plurality(image_bpp, "", "s"));
|
|
|
|
proto_tree_add_item(subtree, &hfi_global_color_map_present,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
2017-05-06 17:37:07 +00:00
|
|
|
proto_tree_add_item(subtree, &hfi_global_color_resolution,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
2017-05-06 17:37:07 +00:00
|
|
|
if (version == GIF_89a) {
|
|
|
|
proto_tree_add_item(subtree, &hfi_global_color_map_ordered,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
2017-05-06 17:37:07 +00:00
|
|
|
}
|
|
|
|
proto_tree_add_item(subtree, &hfi_global_image_bpp,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
|
|
offset++;
|
2012-05-15 19:23:35 +00:00
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
/* Background color */
|
|
|
|
proto_tree_add_item(gif_tree, &hfi_background_color,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
|
|
offset++;
|
2012-05-15 19:23:35 +00:00
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
/* byte at offset 12 is 0x00 - reserved in GIF87a but encodes the
|
|
|
|
* pixel aspect ratio in GIF89a as:
|
|
|
|
* aspect-ratio = (15 + pixel-aspect-ratio) / 64
|
|
|
|
* where the aspect-ratio is not computed if pixel-aspect-ratio == 0
|
|
|
|
*/
|
|
|
|
if (version == GIF_89a) {
|
2017-05-07 12:05:53 +00:00
|
|
|
peek = tvb_get_guint8(tvb, offset);
|
2017-05-06 17:37:07 +00:00
|
|
|
if (peek) {
|
|
|
|
/* Only display if different from 0 */
|
|
|
|
proto_tree_add_uint_format(gif_tree, hfi_pixel_aspect_ratio.id,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, 1, peek,
|
2017-05-06 17:37:07 +00:00
|
|
|
"%u, yields an aspect ratio of (15 + %u) / 64 = %.2f",
|
|
|
|
peek, peek, (float)(15 + peek) / 64.0);
|
2012-05-15 19:23:35 +00:00
|
|
|
}
|
2017-05-06 17:37:07 +00:00
|
|
|
}
|
2017-05-07 12:05:53 +00:00
|
|
|
offset++;
|
2012-05-15 19:23:35 +00:00
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
/* Global color map
|
|
|
|
* If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B)
|
|
|
|
* that contain the Red, Green and Blue intensity of the colors
|
|
|
|
* in the Global Color Map */
|
|
|
|
if (color_map_present) {
|
|
|
|
len = 3 * (1 << image_bpp);
|
|
|
|
proto_tree_add_item(gif_tree, &hfi_global_color_map,
|
2017-05-07 12:05:53 +00:00
|
|
|
tvb, offset, len, ENC_NA);
|
|
|
|
offset += len;
|
2017-05-06 17:37:07 +00:00
|
|
|
}
|
2017-05-07 12:05:53 +00:00
|
|
|
|
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
/* From now on, a set of images prefixed with the image separator
|
|
|
|
* character 0x2C (',') will appear in the byte stream. Each image
|
|
|
|
* hence consists of:
|
|
|
|
* - The image separator character 0x2C
|
|
|
|
* - Image left (16 bits LSB first): pixels from left border
|
|
|
|
* - Image top (16 bits LSB first): pixels from to border
|
|
|
|
* - Image width (16 bits LSB first)
|
|
|
|
* - Image height (16 bits LSB first)
|
|
|
|
* - A bitfield MI00 0ppp
|
|
|
|
* M... .... : Use global color map if unset (ignore ppp);
|
|
|
|
* if set a local color map will be defined.
|
|
|
|
* .I.. .... : Image formatted in interlaced order if set;
|
|
|
|
* otherwise it is plain sequential order
|
|
|
|
* ..0. .... : GIF87a - Reserved
|
|
|
|
* ..s. .... GIF89a - Set if local color map is ordered
|
|
|
|
* ...0 0... : Reserved
|
|
|
|
* .... .ppp : bits per pixel in image (add one)
|
|
|
|
* - If the local color map bit is set, then a local color table follows
|
|
|
|
* with length = 3 x 2 ^ (1 + bits per pixel)
|
|
|
|
* - The raster data
|
|
|
|
*
|
|
|
|
* NOTE that the GIF specification only requires that:
|
|
|
|
* image left + image width <= screen width
|
|
|
|
* image top + image height <= screen height
|
|
|
|
*
|
|
|
|
* The Raster Data is encoded as follows:
|
|
|
|
* - Code size (1 byte)
|
|
|
|
* - Blocks consisting of
|
|
|
|
* o Byte count (1 byte): number of bytes in the block
|
|
|
|
* o Data bytes: as many as specified in the byte count
|
|
|
|
* End of data is given with an empty block (byte count == 0).
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* GIF terminator
|
|
|
|
* This is a synchronization method, based on the final character 0x3B
|
|
|
|
* (';') at the end of an image
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* GIF extension
|
|
|
|
* This is a block of data encoded as:
|
|
|
|
* - The GIF extension block introducer 0x21 ('!')
|
|
|
|
* - The extension function code (1 byte)
|
|
|
|
* - Blocks consisting of
|
|
|
|
* o Byte count (1 byte): number of bytes in the block
|
|
|
|
* o Data bytes: as many as specified in the byte count
|
|
|
|
* End of data is given with an empty block (byte count == 0).
|
|
|
|
*
|
|
|
|
* NOTE that the GIF extension block can only appear at the following
|
|
|
|
* locations:
|
|
|
|
* - Immediately before an Image Descriptor
|
|
|
|
* - Before the GIF termination character
|
|
|
|
*/
|
|
|
|
while (tvb_reported_length_remaining(tvb, offset)) {
|
2017-05-07 12:05:53 +00:00
|
|
|
gint ret;
|
|
|
|
gint offset_start = offset;
|
|
|
|
|
2017-05-06 17:37:07 +00:00
|
|
|
peek = tvb_get_guint8(tvb, offset);
|
|
|
|
if (peek == 0x21) { /* GIF extension block */
|
|
|
|
ti = proto_tree_add_item(gif_tree, &hfi_extension,
|
|
|
|
tvb, offset, 1, ENC_NA);
|
|
|
|
subtree = proto_item_add_subtree(ti, ett_extension);
|
|
|
|
offset++;
|
|
|
|
proto_tree_add_item(subtree, &hfi_extension_label,
|
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
2012-05-15 19:23:35 +00:00
|
|
|
peek = tvb_get_guint8(tvb, offset);
|
2017-05-06 17:37:07 +00:00
|
|
|
proto_item_append_text(ti, ": %s",
|
|
|
|
val_to_str(peek, vals_extensions,
|
|
|
|
"<Warning: Unknown extension 0x%02X>"));
|
|
|
|
offset++;
|
2017-05-07 12:05:53 +00:00
|
|
|
ret = dissect_gif_data_block_seq(tvb, offset, subtree);
|
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
|
|
|
offset += ret;
|
2017-05-06 17:37:07 +00:00
|
|
|
} else if (peek == 0x2C) { /* Image separator */
|
|
|
|
proto_tree *subtree2;
|
|
|
|
proto_item *ti2;
|
|
|
|
|
|
|
|
ti = proto_tree_add_item(gif_tree, &hfi_image,
|
|
|
|
tvb, offset, 1, ENC_NA);
|
|
|
|
subtree = proto_item_add_subtree(ti, ett_image);
|
|
|
|
offset++;
|
|
|
|
/* Screen descriptor */
|
|
|
|
proto_tree_add_item(subtree, &hfi_image_left,
|
|
|
|
tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
|
|
|
|
proto_tree_add_item(subtree, &hfi_image_top,
|
|
|
|
tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
|
|
|
|
proto_tree_add_item(subtree, &hfi_image_width,
|
|
|
|
tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
|
|
|
|
proto_tree_add_item(subtree, &hfi_image_height,
|
|
|
|
tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
|
|
|
|
/* bit field */
|
|
|
|
peek = tvb_get_guint8(tvb, offset);
|
|
|
|
color_map_present = peek & 0x80;
|
|
|
|
color_resolution = 1 + ((peek & 0x60) >> 4);
|
|
|
|
image_bpp = 1 + (peek & 0x07);
|
|
|
|
|
|
|
|
subtree2 = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_local_flags, &ti2,
|
|
|
|
"Local settings:");
|
|
|
|
if (color_map_present)
|
|
|
|
proto_item_append_text(ti2, " (Local color table present)");
|
|
|
|
proto_item_append_text(ti2,
|
|
|
|
" (%u bit%s per color) (%u bit%s per pixel)",
|
|
|
|
color_resolution, plurality(color_resolution, "", "s"),
|
|
|
|
image_bpp, plurality(image_bpp, "", "s"));
|
|
|
|
proto_tree_add_item(subtree2, &hfi_local_color_map_present,
|
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
|
|
proto_tree_add_item(subtree2, &hfi_local_color_resolution,
|
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
|
|
if (version == GIF_89a) {
|
|
|
|
proto_tree_add_item(subtree2, &hfi_local_color_map_ordered,
|
2012-05-15 19:23:35 +00:00
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
2017-05-06 17:37:07 +00:00
|
|
|
}
|
|
|
|
proto_tree_add_item(subtree2, &hfi_global_image_bpp,
|
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
/* Local color map
|
|
|
|
* If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B)
|
|
|
|
* that contain the Red, Green and Blue intensity of the colors
|
|
|
|
* in the Local Color Map */
|
|
|
|
if (color_map_present) {
|
|
|
|
len = 3 * (1 << image_bpp);
|
|
|
|
proto_tree_add_item(subtree, &hfi_local_color_map,
|
|
|
|
tvb, offset, len, ENC_NA);
|
2017-05-07 12:05:53 +00:00
|
|
|
offset += len;
|
2012-05-15 19:23:35 +00:00
|
|
|
}
|
2017-05-06 17:37:07 +00:00
|
|
|
|
|
|
|
proto_tree_add_item(subtree, &hfi_image_code_size,
|
|
|
|
tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
|
|
|
offset++;
|
2017-05-07 12:05:53 +00:00
|
|
|
ret = dissect_gif_data_block_seq(tvb, offset, subtree);
|
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
|
|
|
offset += ret;
|
2017-05-28 08:26:05 +00:00
|
|
|
} else if (peek == 0x3B) { /* Trailer byte */
|
2017-05-06 17:37:07 +00:00
|
|
|
/* GIF processing stops at this very byte */
|
|
|
|
proto_tree_add_item(gif_tree, &hfi_trailer,
|
|
|
|
tvb, offset, 1, ENC_NA);
|
2017-05-28 08:26:05 +00:00
|
|
|
offset++;
|
2017-05-06 17:37:07 +00:00
|
|
|
break;
|
2017-05-28 08:26:05 +00:00
|
|
|
} else {
|
|
|
|
proto_tree_add_expert(gif_tree, pinfo,
|
|
|
|
&ei_gif_unknown_data_block_type,
|
|
|
|
tvb, offset, 1);
|
|
|
|
offset++;
|
2017-05-06 17:37:07 +00:00
|
|
|
}
|
2017-05-07 12:05:53 +00:00
|
|
|
proto_item_set_len(ti, offset-offset_start);
|
2017-05-06 17:37:07 +00:00
|
|
|
} /* while */
|
|
|
|
|
2012-05-15 19:23:35 +00:00
|
|
|
return offset;
|
2003-12-24 02:06:26 +00:00
|
|
|
}
|
|
|
|
|
2011-03-22 16:43:16 +00:00
|
|
|
static gboolean
|
2012-09-10 21:40:21 +00:00
|
|
|
dissect_gif_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
2011-03-22 16:43:16 +00:00
|
|
|
{
|
2012-09-10 21:40:21 +00:00
|
|
|
return dissect_gif(tvb, pinfo, tree, NULL) > 0;
|
2011-03-22 16:43:16 +00:00
|
|
|
}
|
|
|
|
|
2003-12-24 02:06:26 +00:00
|
|
|
|
2006-05-21 04:49:01 +00:00
|
|
|
/****************** Register the protocol with Wireshark ******************/
|
2003-12-24 02:06:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* This format is required because a script is used to build the C function
|
|
|
|
* that calls the protocol registration. */
|
|
|
|
|
|
|
|
void
|
|
|
|
proto_register_gif(void)
|
|
|
|
{
|
2013-11-07 20:14:18 +00:00
|
|
|
#ifndef HAVE_HFI_SECTION_INIT
|
2012-05-15 19:23:35 +00:00
|
|
|
/*
|
|
|
|
* Setup list of header fields.
|
|
|
|
*/
|
2013-08-30 21:31:42 +00:00
|
|
|
static header_field_info *hfi[] = {
|
2012-05-15 19:23:35 +00:00
|
|
|
/*
|
|
|
|
* GIF signature and version
|
|
|
|
*/
|
2013-08-30 21:31:42 +00:00
|
|
|
&hfi_version,
|
2012-05-15 19:23:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Logical screen descriptor
|
|
|
|
*/
|
2013-08-30 21:31:42 +00:00
|
|
|
&hfi_screen_width,
|
|
|
|
&hfi_screen_height,
|
|
|
|
&hfi_global_color_map_present,
|
|
|
|
&hfi_global_color_resolution,
|
|
|
|
&hfi_global_color_map_ordered,
|
|
|
|
&hfi_global_image_bpp,
|
|
|
|
&hfi_background_color,
|
|
|
|
&hfi_pixel_aspect_ratio,
|
|
|
|
&hfi_global_color_map,
|
2012-05-15 19:23:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Local color map (part of image)
|
|
|
|
*/
|
2013-08-30 21:31:42 +00:00
|
|
|
&hfi_local_color_map_present,
|
|
|
|
&hfi_local_color_resolution,
|
|
|
|
&hfi_local_color_map_ordered,
|
|
|
|
/* &hfi_local_image_bpp, */
|
|
|
|
&hfi_local_color_map,
|
2012-05-15 19:23:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Extension
|
|
|
|
*/
|
2013-08-30 21:31:42 +00:00
|
|
|
&hfi_extension,
|
|
|
|
&hfi_extension_label,
|
2012-05-15 19:23:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Image
|
|
|
|
*/
|
2013-08-30 21:31:42 +00:00
|
|
|
&hfi_image,
|
|
|
|
&hfi_image_left,
|
|
|
|
&hfi_image_top,
|
|
|
|
&hfi_image_width,
|
|
|
|
&hfi_image_height,
|
|
|
|
&hfi_image_code_size,
|
2012-05-15 19:23:35 +00:00
|
|
|
/*
|
|
|
|
* Trailer
|
|
|
|
*/
|
2013-08-30 21:31:42 +00:00
|
|
|
&hfi_trailer,
|
2014-10-01 05:49:38 +00:00
|
|
|
|
|
|
|
&hfi_data_block
|
2012-05-15 19:23:35 +00:00
|
|
|
};
|
2013-11-07 20:14:18 +00:00
|
|
|
#endif
|
2012-05-15 19:23:35 +00:00
|
|
|
|
|
|
|
/* Setup protocol subtree array */
|
|
|
|
static gint *ett[] = {
|
|
|
|
&ett_gif,
|
|
|
|
&ett_global_flags,
|
|
|
|
&ett_local_flags,
|
|
|
|
&ett_extension,
|
|
|
|
&ett_image,
|
|
|
|
};
|
|
|
|
|
2017-05-28 08:26:05 +00:00
|
|
|
static ei_register_info ei[] = {
|
|
|
|
{ &ei_gif_unknown_data_block_type,
|
|
|
|
{ "gif.data_block_type.unknown", PI_PROTOCOL, PI_WARN,
|
|
|
|
"Unknown GIF data block type", EXPFILL }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-08-30 21:31:42 +00:00
|
|
|
int proto_gif;
|
|
|
|
|
2017-05-28 08:26:05 +00:00
|
|
|
expert_module_t* expert_gif;
|
|
|
|
|
2012-05-15 19:23:35 +00:00
|
|
|
/* Register the protocol name and description */
|
|
|
|
proto_gif = proto_register_protocol(
|
|
|
|
"Compuserve GIF",
|
|
|
|
"GIF image",
|
|
|
|
IMG_GIF
|
|
|
|
);
|
|
|
|
|
2013-08-30 21:31:42 +00:00
|
|
|
hfi_gif = proto_registrar_get_nth(proto_gif);
|
|
|
|
|
2012-05-15 19:23:35 +00:00
|
|
|
/* Required function calls to register the header fields
|
|
|
|
* and subtrees used */
|
2013-08-30 21:31:42 +00:00
|
|
|
proto_register_fields(proto_gif, hfi, array_length(hfi));
|
2012-05-15 19:23:35 +00:00
|
|
|
proto_register_subtree_array(ett, array_length(ett));
|
2017-05-28 08:26:05 +00:00
|
|
|
expert_gif = expert_register_protocol(proto_gif);
|
|
|
|
expert_register_field_array(expert_gif, ei, array_length(ei));
|
2012-05-15 19:23:35 +00:00
|
|
|
|
2015-12-09 04:04:01 +00:00
|
|
|
gif_handle = register_dissector(IMG_GIF, dissect_gif, proto_gif);
|
2003-12-24 02:06:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
proto_reg_handoff_gif(void)
|
|
|
|
{
|
2012-05-15 19:23:35 +00:00
|
|
|
/* Register the GIF media type */
|
|
|
|
dissector_add_string("media_type", "image/gif", gif_handle);
|
2015-07-13 00:40:31 +00:00
|
|
|
heur_dissector_add("http", dissect_gif_heur, "GIF file in HTTP", "gif_http", hfi_gif->id, HEURISTIC_ENABLE);
|
|
|
|
heur_dissector_add("wtap_file", dissect_gif_heur, "GIF file", "gif_wtap", hfi_gif->id, HEURISTIC_ENABLE);
|
2003-12-24 02:06:26 +00:00
|
|
|
}
|
2014-10-05 19:54:49 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
|
|
|
*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
|
|
*/
|