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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 /* See http://www.w3.org/TR/PNG for specification
30 #define NEW_PROTO_TREE_API
36 #include <epan/packet.h>
37 #include <epan/exceptions.h>
39 static header_field_info
*hfi_png
= NULL
;
41 #define PNG_HFI_INIT HFI_INIT(proto_png)
43 static header_field_info hfi_png_signature PNG_HFI_INIT
= {
44 "PNG Signature", "png.signature", FT_BYTES
, BASE_NONE
,
45 NULL
, 0, NULL
, HFILL
};
47 static header_field_info hfi_png_chunk_data PNG_HFI_INIT
= {
48 "Data", "png.chunk.data", FT_NONE
, BASE_NONE
,
49 NULL
, 0, NULL
, HFILL
};
51 static header_field_info hfi_png_chunk_type PNG_HFI_INIT
= {
52 "Chunk", "png.chunk.type", FT_STRING
, BASE_NONE
,
53 NULL
, 0, NULL
, HFILL
};
55 static header_field_info hfi_png_chunk_len PNG_HFI_INIT
= {
56 "Len", "png.chunk.len", FT_UINT32
, BASE_DEC
,
57 NULL
, 0, NULL
, HFILL
};
59 static header_field_info hfi_png_chunk_crc PNG_HFI_INIT
= {
60 "CRC", "png.chunk.crc", FT_UINT32
, BASE_HEX
,
61 NULL
, 0, NULL
, HFILL
};
63 static const true_false_string png_chunk_anc
= {
64 "This is an ANCILLARY chunk",
65 "This is a CRITICAL chunk"
68 static header_field_info hfi_png_chunk_flag_anc PNG_HFI_INIT
= {
69 "Ancillary", "png.chunk.flag.ancillary", FT_BOOLEAN
, 32,
70 TFS(&png_chunk_anc
), 0x20000000, NULL
, HFILL
};
72 static const true_false_string png_chunk_priv
= {
73 "This is a PRIVATE chunk",
74 "This is a PUBLIC chunk"
77 static header_field_info hfi_png_chunk_flag_priv PNG_HFI_INIT
= {
78 "Private", "png.chunk.flag.private", FT_BOOLEAN
, 32,
79 TFS(&png_chunk_priv
), 0x00200000, NULL
, HFILL
};
81 static const true_false_string png_chunk_stc
= {
82 "This chunk is SAFE TO COPY",
83 "This chunk is NOT safe to copy"
86 static header_field_info hfi_png_chunk_flag_stc PNG_HFI_INIT
= {
87 "Safe To Copy", "png.chunk.flag.stc", FT_BOOLEAN
, 32,
88 TFS(&png_chunk_stc
), 0x00000020, NULL
, HFILL
};
90 static header_field_info hfi_png_ihdr_width PNG_HFI_INIT
= {
91 "Width", "png.ihdr.width", FT_UINT32
, BASE_DEC
,
92 NULL
, 0, NULL
, HFILL
};
94 static header_field_info hfi_png_ihdr_height PNG_HFI_INIT
= {
95 "Height", "png.ihdr.height", FT_UINT32
, BASE_DEC
,
96 NULL
, 0, NULL
, HFILL
};
98 static header_field_info hfi_png_ihdr_bitdepth PNG_HFI_INIT
= {
99 "Bit Depth", "png.ihdr.bitdepth", FT_UINT8
, BASE_DEC
,
100 NULL
, 0, NULL
, HFILL
};
102 static const value_string colour_type_vals
[] = {
105 { 3, "Indexed-colour"},
106 { 4, "Greyscale with alpha"},
107 { 6, "Truecolour with alpha"},
111 static header_field_info hfi_png_ihdr_colour_type PNG_HFI_INIT
= {
112 "Colour Type", "png.ihdr.colour_type", FT_UINT8
, BASE_DEC
,
113 VALS(colour_type_vals
), 0, NULL
, HFILL
};
115 static const value_string compression_method_vals
[] = {
120 static header_field_info hfi_png_ihdr_compression_method PNG_HFI_INIT
= {
121 "Compression Method", "png.ihdr.compression_method", FT_UINT8
, BASE_DEC
,
122 VALS(compression_method_vals
), 0, NULL
, HFILL
};
124 static const value_string filter_method_vals
[] = {
129 static header_field_info hfi_png_ihdr_filter_method PNG_HFI_INIT
= {
130 "Filter Method", "png.ihdr.filter_method", FT_UINT8
, BASE_DEC
,
131 VALS(filter_method_vals
), 0, NULL
, HFILL
};
133 static const value_string interlace_method_vals
[] = {
134 { 0, "No interlace"},
139 static header_field_info hfi_png_ihdr_interlace_method PNG_HFI_INIT
= {
140 "Interlace Method", "png.ihdr.interlace_method", FT_UINT8
, BASE_DEC
,
141 VALS(interlace_method_vals
), 0, NULL
, HFILL
};
143 static header_field_info hfi_png_text_keyword PNG_HFI_INIT
= {
144 "Keyword", "png.text.keyword", FT_STRING
, BASE_NONE
,
145 NULL
, 0, NULL
, HFILL
};
147 static header_field_info hfi_png_text_string PNG_HFI_INIT
= {
148 "String", "png.text.string", FT_STRING
, BASE_NONE
,
149 NULL
, 0, NULL
, HFILL
};
151 static header_field_info hfi_png_time_year PNG_HFI_INIT
= {
152 "Year", "png.time.year", FT_UINT16
, BASE_DEC
,
153 NULL
, 0, NULL
, HFILL
};
155 static header_field_info hfi_png_time_month PNG_HFI_INIT
= {
156 "Month", "png.time.month", FT_UINT8
, BASE_DEC
,
157 NULL
, 0, NULL
, HFILL
};
159 static header_field_info hfi_png_time_day PNG_HFI_INIT
= {
160 "Day", "png.time.day", FT_UINT8
, BASE_DEC
,
161 NULL
, 0, NULL
, HFILL
};
163 static header_field_info hfi_png_time_hour PNG_HFI_INIT
= {
164 "Hour", "png.time.hour", FT_UINT8
, BASE_DEC
,
165 NULL
, 0, NULL
, HFILL
};
167 static header_field_info hfi_png_time_minute PNG_HFI_INIT
= {
168 "Minute", "png.time.minute", FT_UINT8
, BASE_DEC
,
169 NULL
, 0, NULL
, HFILL
};
171 static header_field_info hfi_png_time_second PNG_HFI_INIT
= {
172 "Second", "png.time.second", FT_UINT8
, BASE_DEC
,
173 NULL
, 0, NULL
, HFILL
};
175 static header_field_info hfi_png_phys_horiz PNG_HFI_INIT
= {
176 "Horizontal pixels per unit", "png.phys.horiz", FT_UINT32
, BASE_DEC
,
177 NULL
, 0, NULL
, HFILL
};
179 static header_field_info hfi_png_phys_vert PNG_HFI_INIT
= {
180 "Vertical pixels per unit", "png.phys.vert", FT_UINT32
, BASE_DEC
,
181 NULL
, 0, NULL
, HFILL
};
183 static const value_string phys_unit_vals
[] = {
184 { 0, "Unit is unknown"},
185 { 1, "Unit is METRE"},
189 static header_field_info hfi_png_phys_unit PNG_HFI_INIT
= {
190 "Unit", "png.phys.unit", FT_UINT8
, BASE_DEC
,
191 VALS(phys_unit_vals
), 0, NULL
, HFILL
};
193 static header_field_info hfi_png_bkgd_palette_index PNG_HFI_INIT
= {
194 "Palette Index", "png.bkgd.palette_index", FT_UINT8
, BASE_DEC
,
195 NULL
, 0, NULL
, HFILL
};
197 static header_field_info hfi_png_bkgd_greyscale PNG_HFI_INIT
= {
198 "Greyscale", "png.bkgd.greyscale", FT_UINT16
, BASE_HEX
,
199 NULL
, 0, NULL
, HFILL
};
201 static header_field_info hfi_png_bkgd_red PNG_HFI_INIT
= {
202 "Red", "png.bkgd.red", FT_UINT16
, BASE_HEX
,
203 NULL
, 0, NULL
, HFILL
};
205 static header_field_info hfi_png_bkgd_green PNG_HFI_INIT
= {
206 "Green", "png.bkgd.green", FT_UINT16
, BASE_HEX
,
207 NULL
, 0, NULL
, HFILL
};
209 static header_field_info hfi_png_bkgd_blue PNG_HFI_INIT
= {
210 "Blue", "png.bkgd.blue", FT_UINT16
, BASE_HEX
,
211 NULL
, 0, NULL
, HFILL
};
213 static gint ett_png
= -1;
214 static gint ett_png_chunk
= -1;
215 static gint ett_png_chunk_item
= -1;
217 static dissector_handle_t png_handle
;
220 dissect_png_ihdr(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
)
223 proto_tree_add_item(tree
, &hfi_png_ihdr_width
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
225 proto_tree_add_item(tree
, &hfi_png_ihdr_height
, tvb
, 4, 4, ENC_BIG_ENDIAN
);
227 proto_tree_add_item(tree
, &hfi_png_ihdr_bitdepth
, tvb
, 8, 1, ENC_BIG_ENDIAN
);
229 proto_tree_add_item(tree
, &hfi_png_ihdr_colour_type
, tvb
, 9, 1, ENC_BIG_ENDIAN
);
231 proto_tree_add_item(tree
, &hfi_png_ihdr_compression_method
, tvb
, 10, 1, ENC_BIG_ENDIAN
);
233 proto_tree_add_item(tree
, &hfi_png_ihdr_filter_method
, tvb
, 11, 1, ENC_BIG_ENDIAN
);
235 proto_tree_add_item(tree
, &hfi_png_ihdr_interlace_method
, tvb
, 12, 1, ENC_BIG_ENDIAN
);
240 dissect_png_text(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
)
244 /* find the null that separates keyword and text string */
246 if(!tvb_get_guint8(tvb
, offset
)){
252 proto_tree_add_item(tree
, &hfi_png_text_keyword
, tvb
, 0, offset
, ENC_ASCII
|ENC_NA
);
255 proto_tree_add_item(tree
, &hfi_png_text_string
, tvb
, offset
, tvb_length_remaining(tvb
, offset
), ENC_ASCII
|ENC_NA
);
260 dissect_png_time(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
)
262 proto_tree_add_item(tree
, &hfi_png_time_year
, tvb
, 0, 2, ENC_BIG_ENDIAN
);
263 proto_tree_add_item(tree
, &hfi_png_time_month
, tvb
, 2, 1, ENC_BIG_ENDIAN
);
264 proto_tree_add_item(tree
, &hfi_png_time_day
, tvb
, 3, 1, ENC_BIG_ENDIAN
);
265 proto_tree_add_item(tree
, &hfi_png_time_hour
, tvb
, 4, 1, ENC_BIG_ENDIAN
);
266 proto_tree_add_item(tree
, &hfi_png_time_minute
, tvb
, 5, 1, ENC_BIG_ENDIAN
);
267 proto_tree_add_item(tree
, &hfi_png_time_second
, tvb
, 6, 1, ENC_BIG_ENDIAN
);
271 dissect_png_phys(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
)
273 proto_tree_add_item(tree
, &hfi_png_phys_horiz
, tvb
, 0, 4, ENC_BIG_ENDIAN
);
274 proto_tree_add_item(tree
, &hfi_png_phys_vert
, tvb
, 4, 4, ENC_BIG_ENDIAN
);
275 proto_tree_add_item(tree
, &hfi_png_phys_unit
, tvb
, 8, 1, ENC_BIG_ENDIAN
);
279 dissect_png_bkgd(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
)
281 switch(tvb_reported_length(tvb
)){
282 case 1: /* colour type 3 */
283 proto_tree_add_item(tree
, &hfi_png_bkgd_palette_index
, tvb
, 0, 1, ENC_BIG_ENDIAN
);
285 case 2: /* colour type 0, 4 */
286 proto_tree_add_item(tree
, &hfi_png_bkgd_greyscale
, tvb
, 0, 2, ENC_BIG_ENDIAN
);
288 case 6: /* colour type 2, 6 */
289 proto_tree_add_item(tree
, &hfi_png_bkgd_red
, tvb
, 0, 2, ENC_BIG_ENDIAN
);
290 proto_tree_add_item(tree
, &hfi_png_bkgd_green
, tvb
, 2, 2, ENC_BIG_ENDIAN
);
291 proto_tree_add_item(tree
, &hfi_png_bkgd_blue
, tvb
, 4, 2, ENC_BIG_ENDIAN
);
296 typedef struct _chunk_dissector_t
{
299 void (*dissector
)(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
);
302 static chunk_dissector_t chunk_table
[] = {
303 { 0x49484452, "Image Header", dissect_png_ihdr
}, /* IHDR */
304 { 0x624b4744, "Background colour", dissect_png_bkgd
}, /* bKGD */
305 { 0x70485973, "Physical pixel dimensions",
306 dissect_png_phys
}, /* pHYs */
307 { 0x74455874, "Textual data", dissect_png_text
}, /* tEXt */
308 { 0x74494d45, "Image last-modification time",
309 dissect_png_time
}, /* tIME */
310 { 0x49454e44, "Image Trailer", NULL
}, /* IEND */
315 dissect_png(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void *data _U_
)
317 proto_tree
*tree
= NULL
;
321 /* http://libpng.org/pub/png/spec/1.2/PNG-Structure.html#PNG-file-signature */
322 static const guint8 magic
[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
323 if (tvb_length(tvb
) < 20)
326 if (tvb_memeql(tvb
, 0, magic
, sizeof(magic
)) != 0)
329 col_append_str(pinfo
->cinfo
, COL_INFO
, " (PNG)");
332 ti
=proto_tree_add_item(parent_tree
, hfi_png
, tvb
, offset
, -1, ENC_NA
);
333 tree
=proto_item_add_subtree(ti
, ett_png
);
336 proto_tree_add_item(tree
, &hfi_png_signature
, tvb
, offset
, 8, ENC_NA
);
339 while(tvb_reported_length_remaining(tvb
, offset
) > 0){
340 proto_tree
*chunk_tree
=NULL
;
343 chunk_dissector_t
*cd
;
346 len
=tvb_get_ntohl(tvb
, offset
);
347 type
=tvb_get_ntohl(tvb
, offset
+4);
348 str
[0]=tvb_get_guint8(tvb
, offset
+4);
349 str
[1]=tvb_get_guint8(tvb
, offset
+5);
350 str
[2]=tvb_get_guint8(tvb
, offset
+6);
351 str
[3]=tvb_get_guint8(tvb
, offset
+7);
355 it
=proto_tree_add_text(tree
, tvb
, offset
, offset
+8+len
+4, "%s", str
);
356 chunk_tree
=proto_item_add_subtree(it
, ett_png_chunk
);
359 proto_tree_add_item(chunk_tree
, &hfi_png_chunk_len
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
363 it
=proto_tree_add_item(chunk_tree
, &hfi_png_chunk_type
, tvb
, offset
, 4, ENC_ASCII
|ENC_NA
);
364 proto_tree_add_item(chunk_tree
, &hfi_png_chunk_flag_anc
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
365 proto_tree_add_item(chunk_tree
, &hfi_png_chunk_flag_priv
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
366 proto_tree_add_item(chunk_tree
, &hfi_png_chunk_flag_stc
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
369 if (len
>= 1000000000)
370 THROW(ReportedBoundsError
);
383 proto_item_append_text(chunk_tree
, " %s", cd
?cd
->name
:"(don't know how to dissect this)");
387 proto_tree_add_item(chunk_tree
, &hfi_png_chunk_data
, tvb
, offset
, len
, ENC_NA
);
391 proto_tree
*cti
=NULL
;
393 next_tvb
=tvb_new_subset(tvb
, offset
, MIN(tvb_length_remaining(tvb
, offset
), (int)len
), len
);
395 cti
=proto_item_add_subtree(it
, ett_png_chunk_item
);
397 cd
->dissector(next_tvb
, pinfo
, cti
);
402 proto_tree_add_item(chunk_tree
, &hfi_png_chunk_crc
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
409 proto_register_png(void)
411 #ifndef HAVE_HFI_SECTION_INIT
412 static header_field_info
*hfi
[] =
419 &hfi_png_chunk_flag_anc
,
420 &hfi_png_chunk_flag_priv
,
421 &hfi_png_chunk_flag_stc
,
423 &hfi_png_ihdr_height
,
424 &hfi_png_ihdr_bitdepth
,
425 &hfi_png_ihdr_colour_type
,
426 &hfi_png_ihdr_compression_method
,
427 &hfi_png_ihdr_filter_method
,
428 &hfi_png_ihdr_interlace_method
,
429 &hfi_png_text_keyword
,
430 &hfi_png_text_string
,
435 &hfi_png_time_minute
,
436 &hfi_png_time_second
,
440 &hfi_png_bkgd_palette_index
,
441 &hfi_png_bkgd_greyscale
,
457 proto_png
= proto_register_protocol("Portable Network Graphics","PNG","png");
458 hfi_png
= proto_registrar_get_nth(proto_png
);
460 proto_register_fields(proto_png
, hfi
, array_length(hfi
));
461 proto_register_subtree_array(ett
, array_length(ett
));
463 png_handle
= new_register_dissector("png", dissect_png
, proto_png
);
466 static gboolean
dissect_png_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
468 return dissect_png(tvb
, pinfo
, tree
, NULL
) > 0;
472 proto_reg_handoff_png(void)
474 dissector_add_string("media_type", "image/png", png_handle
);
475 heur_dissector_add("http", dissect_png_heur
, hfi_png
->id
);
476 heur_dissector_add("wtap_file", dissect_png_heur
, hfi_png
->id
);