3 * Routines for PNG (Portable Network Graphics) image file dissection
7 * Copyright 2006 Ronnie Sahlberg
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 /* See http://www.w3.org/TR/PNG for specification
35 #include <epan/packet.h>
38 static int proto_png = -1;
39 static int hf_png_signature = -1;
40 static int hf_png_chunk_data = -1;
41 static int hf_png_chunk_type = -1;
42 static int hf_png_chunk_len = -1;
43 static int hf_png_chunk_crc = -1;
44 static int hf_png_chunk_flag_anc = -1;
45 static int hf_png_chunk_flag_priv = -1;
46 static int hf_png_chunk_flag_stc = -1;
47 static int hf_png_ihdr_width = -1;
48 static int hf_png_ihdr_height = -1;
49 static int hf_png_ihdr_bitdepth = -1;
50 static int hf_png_ihdr_colour_type = -1;
51 static int hf_png_ihdr_compression_method = -1;
52 static int hf_png_ihdr_filter_method = -1;
53 static int hf_png_ihdr_interlace_method = -1;
54 static int hf_png_text_keyword = -1;
55 static int hf_png_text_string = -1;
56 static int hf_png_time_year = -1;
57 static int hf_png_time_month = -1;
58 static int hf_png_time_day = -1;
59 static int hf_png_time_hour = -1;
60 static int hf_png_time_minute = -1;
61 static int hf_png_time_second = -1;
62 static int hf_png_phys_horiz = -1;
63 static int hf_png_phys_vert = -1;
64 static int hf_png_phys_unit = -1;
65 static int hf_png_bkgd_palette_index = -1;
66 static int hf_png_bkgd_greyscale = -1;
67 static int hf_png_bkgd_red = -1;
68 static int hf_png_bkgd_green = -1;
69 static int hf_png_bkgd_blue = -1;
71 static gint ett_png = -1;
72 static gint ett_png_chunk = -1;
73 static gint ett_png_chunk_item = -1;
76 static const value_string colour_type_vals[] = {
79 { 3, "Indexed-colour"},
80 { 4, "Greyscale with alpha"},
81 { 6, "Truecolour with alpha"},
84 static const value_string compression_method_vals[] = {
88 static const value_string filter_method_vals[] = {
92 static const value_string interlace_method_vals[] = {
99 dissect_png_ihdr(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
103 proto_tree_add_item(tree, hf_png_ihdr_width, tvb, offset, 4, ENC_BIG_ENDIAN);
106 proto_tree_add_item(tree, hf_png_ihdr_height, tvb, offset, 4, ENC_BIG_ENDIAN);
109 proto_tree_add_item(tree, hf_png_ihdr_bitdepth, tvb, offset, 1, ENC_BIG_ENDIAN);
112 proto_tree_add_item(tree, hf_png_ihdr_colour_type, tvb, offset, 1, ENC_BIG_ENDIAN);
115 proto_tree_add_item(tree, hf_png_ihdr_compression_method, tvb, offset, 1, ENC_BIG_ENDIAN);
118 proto_tree_add_item(tree, hf_png_ihdr_filter_method, tvb, offset, 1, ENC_BIG_ENDIAN);
121 proto_tree_add_item(tree, hf_png_ihdr_interlace_method, tvb, offset, 1, ENC_BIG_ENDIAN);
128 dissect_png_text(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
132 /* find the null that separates keyword and text string */
134 if(!tvb_get_guint8(tvb, offset)){
140 proto_tree_add_item(tree, hf_png_text_keyword, tvb, 0, offset, ENC_ASCII|ENC_NA);
143 proto_tree_add_item(tree, hf_png_text_string, tvb, offset, tvb_length_remaining(tvb, offset), ENC_ASCII|ENC_NA);
148 dissect_png_time(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
150 proto_tree_add_item(tree, hf_png_time_year, tvb, 0, 2, ENC_BIG_ENDIAN);
151 proto_tree_add_item(tree, hf_png_time_month, tvb, 2, 1, ENC_BIG_ENDIAN);
152 proto_tree_add_item(tree, hf_png_time_day, tvb, 3, 1, ENC_BIG_ENDIAN);
153 proto_tree_add_item(tree, hf_png_time_hour, tvb, 4, 1, ENC_BIG_ENDIAN);
154 proto_tree_add_item(tree, hf_png_time_minute, tvb, 5, 1, ENC_BIG_ENDIAN);
155 proto_tree_add_item(tree, hf_png_time_second, tvb, 6, 1, ENC_BIG_ENDIAN);
158 static const value_string phys_unit_vals[] = {
159 { 0, "Unit is unknown"},
160 { 1, "Unit is METRE"},
164 dissect_png_phys(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
166 proto_tree_add_item(tree, hf_png_phys_horiz, tvb, 0, 4, ENC_BIG_ENDIAN);
167 proto_tree_add_item(tree, hf_png_phys_vert, tvb, 4, 4, ENC_BIG_ENDIAN);
168 proto_tree_add_item(tree, hf_png_phys_unit, tvb, 8, 1, ENC_BIG_ENDIAN);
172 dissect_png_bkgd(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
174 switch(tvb_reported_length(tvb)){
175 case 1: /* colour type 3 */
176 proto_tree_add_item(tree, hf_png_bkgd_palette_index, tvb, 0, 1, ENC_BIG_ENDIAN);
178 case 2: /* colour type 0, 4 */
179 proto_tree_add_item(tree, hf_png_bkgd_greyscale, tvb, 0, 2, ENC_BIG_ENDIAN);
181 case 6: /* colour type 2, 6 */
182 proto_tree_add_item(tree, hf_png_bkgd_red, tvb, 0, 2, ENC_BIG_ENDIAN);
183 proto_tree_add_item(tree, hf_png_bkgd_green, tvb, 2, 2, ENC_BIG_ENDIAN);
184 proto_tree_add_item(tree, hf_png_bkgd_blue, tvb, 4, 2, ENC_BIG_ENDIAN);
189 typedef struct _chunk_dissector_t {
192 void (*dissector)(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
195 static chunk_dissector_t chunk_table[] = {
196 { 0x49484452, "Image Header", dissect_png_ihdr }, /* IHDR */
197 { 0x624b4744, "Background colour", dissect_png_bkgd }, /* bKGD */
198 { 0x70485973, "Physical pixel dimensions",
199 dissect_png_phys }, /* pHYs */
200 { 0x74455874, "Textual data", dissect_png_text }, /* tEXt */
201 { 0x74494d45, "Image last-modification time",
202 dissect_png_time }, /* tIME */
203 { 0x49454e44, "Image Trailer", NULL }, /* IEND */
207 static const true_false_string png_chunk_anc = {
208 "This is an ANCILLARY chunk",
209 "This is a CRITICAL chunk"
211 static const true_false_string png_chunk_priv = {
212 "This is a PRIVATE chunk",
213 "This is a PUBLIC chunk"
215 static const true_false_string png_chunk_stc = {
216 "This chunk is SAFE TO COPY",
217 "This chunk is NOT safe to copy"
222 dissect_png(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
224 proto_tree *tree = NULL;
228 /* http://libpng.org/pub/png/spec/1.2/PNG-Structure.html#PNG-file-signature */
229 static const guint8 magic[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
230 if (tvb_length(tvb) < 20)
233 if (tvb_memeql(tvb, 0, magic, sizeof(magic)) != 0)
236 col_append_str(pinfo->cinfo, COL_INFO, " (PNG)");
239 ti=proto_tree_add_item(parent_tree, proto_png, tvb, offset, -1, FALSE);
240 tree=proto_item_add_subtree(ti, ett_png);
243 proto_tree_add_item(tree, hf_png_signature, tvb, offset, 8, ENC_NA);
246 while(tvb_reported_length_remaining(tvb, offset)){
247 proto_tree *chunk_tree=NULL;
250 chunk_dissector_t *cd;
253 len=tvb_get_ntohl(tvb, offset);
254 type=tvb_get_ntohl(tvb, offset+4);
255 str[0]=tvb_get_guint8(tvb, offset+4);
256 str[1]=tvb_get_guint8(tvb, offset+5);
257 str[2]=tvb_get_guint8(tvb, offset+6);
258 str[3]=tvb_get_guint8(tvb, offset+7);
262 it=proto_tree_add_text(tree, tvb, offset, offset+8+len+4, "%s", str);
263 chunk_tree=proto_item_add_subtree(it, ett_png_chunk);
266 proto_tree_add_item(chunk_tree, hf_png_chunk_len, tvb, offset, 4, ENC_BIG_ENDIAN);
270 it=proto_tree_add_item(chunk_tree, hf_png_chunk_type, tvb, offset, 4, ENC_ASCII|ENC_NA);
271 proto_tree_add_item(chunk_tree, hf_png_chunk_flag_anc, tvb, offset, 4, ENC_BIG_ENDIAN);
272 proto_tree_add_item(chunk_tree, hf_png_chunk_flag_priv, tvb, offset, 4, ENC_BIG_ENDIAN);
273 proto_tree_add_item(chunk_tree, hf_png_chunk_flag_stc, tvb, offset, 4, ENC_BIG_ENDIAN);
276 if (len >= 1000000000)
277 THROW(ReportedBoundsError);
290 proto_item_append_text(chunk_tree, " %s", cd?cd->name:"(don't know how to dissect this)");
294 proto_tree_add_item(chunk_tree, hf_png_chunk_data, tvb, offset, len, ENC_NA);
298 proto_tree *cti=NULL;
300 next_tvb=tvb_new_subset(tvb, offset, MIN(tvb_length_remaining(tvb, offset), (int)len), len);
302 cti=proto_item_add_subtree(it, ett_png_chunk_item);
304 cd->dissector(next_tvb, pinfo, cti);
309 proto_tree_add_item(chunk_tree, hf_png_chunk_crc, tvb, offset, 4, ENC_BIG_ENDIAN);
316 proto_register_png(void)
318 static hf_register_info hf[] =
320 { &hf_png_signature, {
321 "PNG Signature", "png.signature", FT_BYTES, BASE_NONE,
322 NULL, 0, NULL, HFILL }},
323 { &hf_png_chunk_type, {
324 "Chunk", "png.chunk.type", FT_STRING, BASE_NONE,
325 NULL, 0, NULL, HFILL }},
326 { &hf_png_chunk_data, {
327 "Data", "png.chunk.data", FT_NONE, BASE_NONE,
328 NULL, 0, NULL, HFILL }},
329 { &hf_png_chunk_len, {
330 "Len", "png.chunk.len", FT_UINT32, BASE_DEC,
331 NULL, 0, NULL, HFILL }},
332 { &hf_png_chunk_crc, {
333 "CRC", "png.chunk.crc", FT_UINT32, BASE_HEX,
334 NULL, 0, NULL, HFILL }},
335 { &hf_png_chunk_flag_anc, {
336 "Ancillary", "png.chunk.flag.ancillary", FT_BOOLEAN, 32,
337 TFS(&png_chunk_anc), 0x20000000, NULL, HFILL }},
338 { &hf_png_chunk_flag_priv, {
339 "Private", "png.chunk.flag.private", FT_BOOLEAN, 32,
340 TFS(&png_chunk_priv), 0x00200000, NULL, HFILL }},
341 { &hf_png_chunk_flag_stc, {
342 "Safe To Copy", "png.chunk.flag.stc", FT_BOOLEAN, 32,
343 TFS(&png_chunk_stc), 0x00000020, NULL, HFILL }},
344 { &hf_png_ihdr_width, {
345 "Width", "png.ihdr.width", FT_UINT32, BASE_DEC,
346 NULL, 0, NULL, HFILL }},
347 { &hf_png_ihdr_height, {
348 "Height", "png.ihdr.height", FT_UINT32, BASE_DEC,
349 NULL, 0, NULL, HFILL }},
350 { &hf_png_ihdr_bitdepth, {
351 "Bit Depth", "png.ihdr.bitdepth", FT_UINT8, BASE_DEC,
352 NULL, 0, NULL, HFILL }},
353 { &hf_png_ihdr_colour_type, {
354 "Colour Type", "png.ihdr.colour_type", FT_UINT8, BASE_DEC,
355 VALS(colour_type_vals), 0, NULL, HFILL }},
356 { &hf_png_ihdr_compression_method, {
357 "Compression Method", "png.ihdr.compression_method", FT_UINT8, BASE_DEC,
358 VALS(compression_method_vals), 0, NULL, HFILL }},
359 { &hf_png_ihdr_filter_method, {
360 "Filter Method", "png.ihdr.filter_method", FT_UINT8, BASE_DEC,
361 VALS(filter_method_vals), 0, NULL, HFILL }},
362 { &hf_png_ihdr_interlace_method, {
363 "Interlace Method", "png.ihdr.interlace_method", FT_UINT8, BASE_DEC,
364 VALS(interlace_method_vals), 0, NULL, HFILL }},
365 { &hf_png_text_keyword, {
366 "Keyword", "png.text.keyword", FT_STRING, BASE_NONE,
367 NULL, 0, NULL, HFILL }},
368 { &hf_png_text_string, {
369 "String", "png.text.string", FT_STRING, BASE_NONE,
370 NULL, 0, NULL, HFILL }},
371 { &hf_png_time_year, {
372 "Year", "png.time.year", FT_UINT16, BASE_DEC,
373 NULL, 0, NULL, HFILL }},
374 { &hf_png_time_month, {
375 "Month", "png.time.month", FT_UINT8, BASE_DEC,
376 NULL, 0, NULL, HFILL }},
377 { &hf_png_time_day, {
378 "Day", "png.time.day", FT_UINT8, BASE_DEC,
379 NULL, 0, NULL, HFILL }},
380 { &hf_png_time_hour, {
381 "Hour", "png.time.hour", FT_UINT8, BASE_DEC,
382 NULL, 0, NULL, HFILL }},
383 { &hf_png_time_minute, {
384 "Minute", "png.time.minute", FT_UINT8, BASE_DEC,
385 NULL, 0, NULL, HFILL }},
386 { &hf_png_time_second, {
387 "Second", "png.time.second", FT_UINT8, BASE_DEC,
388 NULL, 0, NULL, HFILL }},
389 { &hf_png_phys_horiz, {
390 "Horizontal pixels per unit", "png.phys.horiz", FT_UINT32, BASE_DEC,
391 NULL, 0, NULL, HFILL }},
392 { &hf_png_phys_vert, {
393 "Vertical pixels per unit", "png.phys.vert", FT_UINT32, BASE_DEC,
394 NULL, 0, NULL, HFILL }},
395 { &hf_png_phys_unit, {
396 "Unit", "png.phys.unit", FT_UINT8, BASE_DEC,
397 VALS(phys_unit_vals), 0, NULL, HFILL }},
398 { &hf_png_bkgd_palette_index, {
399 "Palette Index", "png.bkgd.palette_index", FT_UINT8, BASE_DEC,
400 NULL, 0, NULL, HFILL }},
401 { &hf_png_bkgd_greyscale, {
402 "Greyscale", "png.bkgd.greyscale", FT_UINT16, BASE_HEX,
403 NULL, 0, NULL, HFILL }},
404 { &hf_png_bkgd_red, {
405 "Red", "png.bkgd.red", FT_UINT16, BASE_HEX,
406 NULL, 0, NULL, HFILL }},
407 { &hf_png_bkgd_green, {
408 "Green", "png.bkgd.green", FT_UINT16, BASE_HEX,
409 NULL, 0, NULL, HFILL }},
410 { &hf_png_bkgd_blue, {
411 "Blue", "png.bkgd.blue", FT_UINT16, BASE_HEX,
412 NULL, 0, NULL, HFILL }},
423 proto_png = proto_register_protocol("Portable Network Graphics","PNG","png");
424 proto_register_field_array(proto_png, hf, array_length(hf));
425 proto_register_subtree_array(ett, array_length(ett));
426 new_register_dissector("png", dissect_png, proto_png);
429 static gboolean dissect_png_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
431 return dissect_png(tvb, pinfo, tree) > 0;
435 proto_reg_handoff_png(void)
437 dissector_handle_t png_handle = new_create_dissector_handle(dissect_png, proto_png);
438 dissector_add_string("media_type", "image/png", png_handle);
439 heur_dissector_add("http", dissect_png_heur, proto_png);