HACK: 2nd try to match RowsetProperties
[wireshark-wip.git] / epan / dissectors / packet-image-png.c
blobe7cda7533b268760a49e25b9a6089a9a817ab555
1 /* packet-image-png.c
3 * Routines for PNG (Portable Network Graphics) image file dissection
5 * $Id$
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
32 #include "config.h"
34 #include <glib.h>
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[] = {
103 { 0, "Greyscale"},
104 { 2, "Truecolour"},
105 { 3, "Indexed-colour"},
106 { 4, "Greyscale with alpha"},
107 { 6, "Truecolour with alpha"},
108 { 0, NULL }
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[] = {
116 { 0, "Deflate"},
117 { 0, NULL }
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[] = {
125 { 0, "Adaptive"},
126 { 0, NULL }
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"},
135 { 1, "Adam7"},
136 { 0, NULL }
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"},
186 { 0, NULL }
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;
219 static void
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);
239 static void
240 dissect_png_text(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
242 int offset=1;
244 /* find the null that separates keyword and text string */
245 while(1){
246 if(!tvb_get_guint8(tvb, offset)){
247 break;
249 offset++;
252 proto_tree_add_item(tree, &hfi_png_text_keyword, tvb, 0, offset, ENC_ASCII|ENC_NA);
253 offset++;
255 proto_tree_add_item(tree, &hfi_png_text_string, tvb, offset, tvb_length_remaining(tvb, offset), ENC_ASCII|ENC_NA);
259 static void
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);
270 static void
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);
278 static void
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);
284 break;
285 case 2: /* colour type 0, 4 */
286 proto_tree_add_item(tree, &hfi_png_bkgd_greyscale, tvb, 0, 2, ENC_BIG_ENDIAN);
287 break;
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);
292 break;
296 typedef struct _chunk_dissector_t {
297 guint32 type;
298 const char *name;
299 void (*dissector)(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
300 } chunk_dissector_t;
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 */
311 { 0, NULL, NULL }
314 static gint
315 dissect_png(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
317 proto_tree *tree = NULL;
318 proto_item *ti;
319 int offset=0;
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)
324 return 0;
326 if (tvb_memeql(tvb, 0, magic, sizeof(magic)) != 0)
327 return 0;
329 col_append_str(pinfo->cinfo, COL_INFO, " (PNG)");
331 if(parent_tree){
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);
337 offset+=8;
339 while(tvb_reported_length_remaining(tvb, offset) > 0){
340 proto_tree *chunk_tree=NULL;
341 proto_item *it=NULL;
342 guint32 len, type;
343 chunk_dissector_t *cd;
344 char str[5];
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);
352 str[4]=0;
354 if(tree){
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);
360 offset+=4;
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);
367 offset+=4;
369 if (len >= 1000000000)
370 THROW(ReportedBoundsError);
371 cd=&chunk_table[0];
372 while(1){
373 if(cd->type==0){
374 cd=NULL;
375 break;
377 if(cd->type==type){
378 break;
380 cd++;
382 if(chunk_tree){
383 proto_item_append_text(chunk_tree, " %s", cd?cd->name:"(don't know how to dissect this)");
386 if(!cd){
387 proto_tree_add_item(chunk_tree, &hfi_png_chunk_data, tvb, offset, len, ENC_NA);
388 } else {
389 if(cd->dissector){
390 tvbuff_t *next_tvb;
391 proto_tree *cti=NULL;
393 next_tvb=tvb_new_subset(tvb, offset, MIN(tvb_length_remaining(tvb, offset), (int)len), len);
394 if(it){
395 cti=proto_item_add_subtree(it, ett_png_chunk_item);
397 cd->dissector(next_tvb, pinfo, cti);
400 offset+=len;
402 proto_tree_add_item(chunk_tree, &hfi_png_chunk_crc, tvb, offset, 4, ENC_BIG_ENDIAN);
403 offset+=4;
405 return offset;
408 void
409 proto_register_png(void)
411 #ifndef HAVE_HFI_SECTION_INIT
412 static header_field_info *hfi[] =
414 &hfi_png_signature,
415 &hfi_png_chunk_type,
416 &hfi_png_chunk_data,
417 &hfi_png_chunk_len,
418 &hfi_png_chunk_crc,
419 &hfi_png_chunk_flag_anc,
420 &hfi_png_chunk_flag_priv,
421 &hfi_png_chunk_flag_stc,
422 &hfi_png_ihdr_width,
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,
431 &hfi_png_time_year,
432 &hfi_png_time_month,
433 &hfi_png_time_day,
434 &hfi_png_time_hour,
435 &hfi_png_time_minute,
436 &hfi_png_time_second,
437 &hfi_png_phys_horiz,
438 &hfi_png_phys_vert,
439 &hfi_png_phys_unit,
440 &hfi_png_bkgd_palette_index,
441 &hfi_png_bkgd_greyscale,
442 &hfi_png_bkgd_red,
443 &hfi_png_bkgd_green,
444 &hfi_png_bkgd_blue,
446 #endif
448 static gint *ett[] =
450 &ett_png,
451 &ett_png_chunk,
452 &ett_png_chunk_item,
455 int proto_png;
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;
471 void
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);