1 /* packet-imf.c
2  * Routines for Internet Message Format (IMF) packet disassembly
3  *
4  * Copyright (c) 2007 by Graeme Lunt
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1999 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "config.h"
14 
15 #include <epan/packet.h>
16 #include <epan/prefs.h>
17 #include <epan/uat.h>
18 #include <epan/expert.h>
19 #include <wsutil/str_util.h>
20 
21 #include <epan/tap.h>
22 #include <epan/export_object.h>
23 
24 #include "packet-ber.h"
25 #include "packet-http.h"
26 #include "packet-imf.h"
27 #include "packet-ess.h"
28 #include "packet-p1.h"
29 
30 void proto_register_imf(void);
31 void proto_reg_handoff_imf(void);
32 
33 static int imf_eo_tap = -1;
34 
35 #define PNAME  "Internet Message Format"
36 #define PSNAME "IMF"
37 #define PFNAME "imf"
38 
39 static int proto_imf = -1;
40 
41 static int hf_imf_date = -1;
42 static int hf_imf_from = -1;
43 static int hf_imf_sender = -1;
44 static int hf_imf_reply_to = -1;
45 static int hf_imf_to = -1;
46 static int hf_imf_cc = -1;
47 static int hf_imf_bcc = -1;
48 static int hf_imf_message_id = -1;
49 static int hf_imf_in_reply_to = -1;
50 static int hf_imf_references = -1;
51 static int hf_imf_subject = -1;
52 static int hf_imf_comments = -1;
53 static int hf_imf_user_agent = -1;
54 static int hf_imf_keywords = -1;
55 static int hf_imf_resent_date = -1;
56 static int hf_imf_resent_from = -1;
57 static int hf_imf_resent_sender = -1;
58 static int hf_imf_resent_to = -1;
59 static int hf_imf_resent_cc = -1;
60 static int hf_imf_resent_bcc = -1;
61 static int hf_imf_resent_message_id = -1;
62 static int hf_imf_return_path = -1;
63 static int hf_imf_received = -1;
64 static int hf_imf_content_type = -1;
65 static int hf_imf_content_type_type = -1;
66 static int hf_imf_content_type_parameters = -1;
67 static int hf_imf_content_id = -1;
68 static int hf_imf_content_transfer_encoding = -1;
69 static int hf_imf_content_description = -1;
70 static int hf_imf_mime_version = -1;
71 static int hf_imf_thread_index = -1;
72 static int hf_imf_ext_mailer = -1;
73 static int hf_imf_ext_mimeole = -1;
74 static int hf_imf_ext_tnef_correlator = -1;
75 static int hf_imf_ext_expiry_date = -1;
76 static int hf_imf_ext_uidl = -1;
77 static int hf_imf_ext_authentication_warning = -1;
78 static int hf_imf_ext_virus_scanned = -1;
79 static int hf_imf_extension = -1;
80 static int hf_imf_extension_type = -1;
81 static int hf_imf_extension_value = -1;
82 
83 /* RFC 2156 */
84 static int hf_imf_autoforwarded = -1;
85 static int hf_imf_autosubmitted = -1;
86 static int hf_imf_x400_content_identifier = -1;
87 static int hf_imf_content_language = -1;
88 static int hf_imf_conversion = -1;
89 static int hf_imf_conversion_with_loss = -1;
90 static int hf_imf_delivery_date = -1;
91 static int hf_imf_discarded_x400_ipms_extensions = -1;
92 static int hf_imf_discarded_x400_mts_extensions = -1;
93 static int hf_imf_dl_expansion_history = -1;
94 static int hf_imf_deferred_delivery = -1;
95 static int hf_imf_expires = -1;
96 static int hf_imf_importance = -1;
97 static int hf_imf_incomplete_copy = -1;
98 static int hf_imf_latest_delivery_time = -1;
99 static int hf_imf_message_type = -1;
100 static int hf_imf_original_encoded_information_types = -1;
101 static int hf_imf_originator_return_address = -1;
102 static int hf_imf_priority = -1;
103 static int hf_imf_reply_by = -1;
104 static int hf_imf_sensitivity = -1;
105 static int hf_imf_supersedes = -1;
106 static int hf_imf_x400_content_type = -1;
107 static int hf_imf_x400_mts_identifier = -1;
108 static int hf_imf_x400_originator = -1;
109 static int hf_imf_x400_received = -1;
110 static int hf_imf_x400_recipients = -1;
111 
112 static int hf_imf_delivered_to = -1;
113 
114 static int hf_imf_message_text = -1;
115 
116 static int hf_imf_display_name = -1;
117 static int hf_imf_address = -1;
118 /* static int hf_imf_mailbox_list = -1; */
119 static int hf_imf_mailbox_list_item = -1;
120 /* static int hf_imf_address_list = -1; */
121 static int hf_imf_address_list_item = -1;
122 
123 /* draft-zeilenga-email-seclabel-04 */
124 static int hf_imf_siolabel = -1;
125 static int hf_imf_siolabel_marking = -1;
126 static int hf_imf_siolabel_fgcolor = -1;
127 static int hf_imf_siolabel_bgcolor = -1;
128 static int hf_imf_siolabel_type = -1;
129 static int hf_imf_siolabel_label = -1;
130 static int hf_imf_siolabel_unknown = -1;
131 
132 static int ett_imf = -1;
133 static int ett_imf_content_type = -1;
134 static int ett_imf_mailbox = -1;
135 static int ett_imf_group = -1;
136 static int ett_imf_mailbox_list = -1;
137 static int ett_imf_address_list = -1;
138 static int ett_imf_siolabel = -1;
139 static int ett_imf_extension = -1;
140 static int ett_imf_message_text = -1;
141 
142 static dissector_handle_t imf_handle;
143 
144 static expert_field ei_imf_unknown_param = EI_INIT;
145 
146 /* Used for IMF Export Object feature */
147 typedef struct _imf_eo_t {
148   gchar    *filename;
149   gchar    *sender_data;
150   gchar    *subject_data;
151   guint32  payload_len;
152   gchar    *payload_data;
153 } imf_eo_t;
154 
155 static tap_packet_status
imf_eo_packet(void * tapdata,packet_info * pinfo,epan_dissect_t * edt _U_,const void * data)156 imf_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data)
157 {
158   export_object_list_t *object_list = (export_object_list_t *)tapdata;
159   const imf_eo_t *eo_info = (const imf_eo_t *)data;
160   export_object_entry_t *entry;
161 
162   if(eo_info) { /* We have data waiting for us */
163     /* These values will be freed when the Export Object window
164      * is closed. */
165     entry = g_new(export_object_entry_t, 1);
166 
167     gchar *start = g_strrstr_len(eo_info->sender_data, -1, "<");
168     gchar *stop = g_strrstr_len(eo_info->sender_data, -1,  ">");
169     /* Only include the string inside of the "<>" brackets. If there is nothing between
170     the two brackets use the sender_data string */
171     if(start && stop && stop > start && (stop - start) > 2){
172         entry->hostname = g_strdup_printf("%.*s", (int) (stop - start - 1), start + 1);
173     } else {
174         entry->hostname = g_strdup(eo_info->sender_data);
175     }
176 
177     entry->pkt_num = pinfo->num;
178     entry->content_type = g_strdup("EML file");
179     entry->filename = g_strdup_printf("%s.eml", eo_info->subject_data);
180     entry->payload_len = eo_info->payload_len;
181     entry->payload_data = (guint8 *)g_memdup2(eo_info->payload_data, eo_info->payload_len);
182 
183     object_list->add_entry(object_list->gui_data, entry);
184 
185     return TAP_PACKET_REDRAW; /* State changed - window should be redrawn */
186   } else {
187     return TAP_PACKET_DONT_REDRAW; /* State unchanged - no window updates needed */
188   }
189 }
190 
191 
192 struct imf_field {
193   char         *name;           /* field name - in lower case for matching purposes */
194   int          *hf_id;          /* wireshark field */
195   void         (*subdissector)(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo);
196   gboolean     add_to_col_info; /* add field to column info */
197 };
198 
199 #define NO_SUBDISSECTION NULL
200 
201 static void dissect_imf_mailbox(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo);
202 static void dissect_imf_address(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo);
203 static void dissect_imf_address_list(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo);
204 static void dissect_imf_mailbox_list(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo);
205 static void dissect_imf_siolabel(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo);
206 
207 static struct imf_field imf_fields[] = {
208   {"unknown-extension",                   &hf_imf_extension_type, NO_SUBDISSECTION, FALSE}, /* unknown extension */
209   {"date",                                &hf_imf_date, NO_SUBDISSECTION, FALSE}, /* date-time */
210   {"from",                                &hf_imf_from, dissect_imf_mailbox_list , TRUE}, /* mailbox_list */
211   {"sender",                              &hf_imf_sender, dissect_imf_mailbox, FALSE}, /* mailbox */
212   {"reply-to",                            &hf_imf_reply_to, dissect_imf_address_list , FALSE}, /* address_list */
213   {"to",                                  &hf_imf_to, dissect_imf_address_list , FALSE}, /* address_list */
214   {"cc",                                  &hf_imf_cc, dissect_imf_address_list , FALSE}, /* address_list */
215   {"bcc",                                 &hf_imf_bcc, dissect_imf_address_list , FALSE}, /* address_list */
216   {"message-id",                          &hf_imf_message_id, NO_SUBDISSECTION, FALSE}, /* msg-id */
217   {"in-reply-to",                         &hf_imf_in_reply_to, NO_SUBDISSECTION, FALSE}, /* msg-id */
218   {"references",                          &hf_imf_references, NO_SUBDISSECTION, FALSE}, /* msg-id */
219   {"subject",                             &hf_imf_subject, NO_SUBDISSECTION, TRUE}, /* unstructured */
220   {"comments",                            &hf_imf_comments, NO_SUBDISSECTION, FALSE}, /* unstructured */
221   {"user-agent",                          &hf_imf_user_agent, NO_SUBDISSECTION, FALSE}, /* unstructured */
222   {"keywords",                            &hf_imf_keywords, NULL, FALSE}, /* phrase_list */
223   {"resent-date",                         &hf_imf_resent_date, NO_SUBDISSECTION, FALSE},
224   {"resent-from",                         &hf_imf_resent_from, dissect_imf_mailbox_list, FALSE},
225   {"resent-sender",                       &hf_imf_resent_sender, dissect_imf_mailbox, FALSE},
226   {"resent-to",                           &hf_imf_resent_to, dissect_imf_address_list, FALSE},
227   {"resent-cc",                           &hf_imf_resent_cc, dissect_imf_address_list, FALSE},
228   {"resent-bcc",                          &hf_imf_resent_bcc, dissect_imf_address_list, FALSE},
229   {"resent-message-id",                   &hf_imf_resent_message_id, NO_SUBDISSECTION, FALSE},
230   {"return-path",                         &hf_imf_return_path, NULL, FALSE},
231   {"received",                            &hf_imf_received, NO_SUBDISSECTION, FALSE},
232   /* these are really multi-part - but we parse them anyway */
233   {"content-type",                        &hf_imf_content_type, NULL, FALSE}, /* handled separately as a special case */
234   {"content-id",                          &hf_imf_content_id, NULL, FALSE},
235   {"content-description",                 &hf_imf_content_description, NULL, FALSE},
236   {"content-transfer-encoding",           &hf_imf_content_transfer_encoding, NULL, FALSE},
237   {"mime-version",                        &hf_imf_mime_version, NO_SUBDISSECTION, FALSE},
238   /* MIXER - RFC 2156 */
239   {"autoforwarded",                       &hf_imf_autoforwarded, NULL, FALSE},
240   {"autosubmitted",                       &hf_imf_autosubmitted, NULL, FALSE},
241   {"x400-content-identifier",             &hf_imf_x400_content_identifier, NULL, FALSE},
242   {"content-language",                    &hf_imf_content_language, NULL, FALSE},
243   {"conversion",                          &hf_imf_conversion, NULL, FALSE},
244   {"conversion-with-loss",                &hf_imf_conversion_with_loss, NULL, FALSE},
245   {"delivery-date",                       &hf_imf_delivery_date, NULL, FALSE},
246   {"discarded-x400-ipms-extensions",      &hf_imf_discarded_x400_ipms_extensions, NULL, FALSE},
247   {"discarded-x400-mts-extensions",       &hf_imf_discarded_x400_mts_extensions, NULL, FALSE},
248   {"dl-expansion-history",                &hf_imf_dl_expansion_history, NULL, FALSE},
249   {"deferred-delivery",                   &hf_imf_deferred_delivery, NULL, FALSE},
250   {"expires",                             &hf_imf_expires, NULL, FALSE},
251   {"importance",                          &hf_imf_importance, NULL, FALSE},
252   {"incomplete-copy",                     &hf_imf_incomplete_copy, NULL, FALSE},
253   {"latest-delivery-time",                &hf_imf_latest_delivery_time, NULL, FALSE},
254   {"message-type",                        &hf_imf_message_type, NULL, FALSE},
255   {"original-encoded-information-types",  &hf_imf_original_encoded_information_types, NULL, FALSE},
256   {"originator-return-address",           &hf_imf_originator_return_address, NULL, FALSE},
257   {"priority",                            &hf_imf_priority, NULL, FALSE},
258   {"reply-by",                            &hf_imf_reply_by, NULL, FALSE},
259   {"sensitivity",                         &hf_imf_sensitivity, NULL, FALSE},
260   {"supersedes",                          &hf_imf_supersedes, NULL, FALSE},
261   {"x400-content-type",                   &hf_imf_x400_content_type, NULL, FALSE},
262   {"x400-mts-identifier",                 &hf_imf_x400_mts_identifier, NULL, FALSE},
263   {"x400-originator",                     &hf_imf_x400_originator, NULL, FALSE},
264   {"x400-received",                       &hf_imf_x400_received, NULL, FALSE},
265   {"x400-recipients",                     &hf_imf_x400_recipients, NULL, FALSE},
266   /* delivery */
267   {"delivered-to",                        &hf_imf_delivered_to, dissect_imf_mailbox, FALSE}, /* mailbox */
268   /* some others */
269   {"x-mailer",                            &hf_imf_ext_mailer, NO_SUBDISSECTION, FALSE}, /* unstructured */
270   {"thread-index",                        &hf_imf_thread_index, NO_SUBDISSECTION, FALSE}, /* unstructured */
271   {"x-mimeole",                           &hf_imf_ext_mimeole, NO_SUBDISSECTION, FALSE}, /* unstructured */
272   {"expiry-date",                         &hf_imf_ext_expiry_date, NO_SUBDISSECTION, FALSE}, /* unstructured */
273   {"x-ms-tnef-correlator",                &hf_imf_ext_tnef_correlator, NO_SUBDISSECTION, FALSE}, /* unstructured */
274   {"x-uidl",                              &hf_imf_ext_uidl, NO_SUBDISSECTION, FALSE}, /* unstructured */
275   {"x-authentication-warning",            &hf_imf_ext_authentication_warning, NO_SUBDISSECTION, FALSE}, /* unstructured */
276   {"x-virus-scanned",                     &hf_imf_ext_virus_scanned, NO_SUBDISSECTION, FALSE}, /* unstructured */
277   {"sio-label",                           &hf_imf_siolabel, dissect_imf_siolabel, FALSE}, /* sio-label */
278   {NULL, NULL, NULL, FALSE},
279 };
280 
281 static wmem_map_t *imf_field_table=NULL;
282 
283 #define FORMAT_UNSTRUCTURED  0
284 #define FORMAT_MAILBOX       1
285 #define FORMAT_ADDRESS       2
286 #define FORMAT_MAILBOX_LIST  3
287 #define FORMAT_ADDRESS_LIST  4
288 #define FORMAT_SIO_LABEL     5
289 
290 static const value_string header_format[] = {
291   { FORMAT_UNSTRUCTURED, "Unstructured" },
292   { FORMAT_MAILBOX,      "Mailbox"      },
293   { FORMAT_ADDRESS,      "Address"      },
294   { FORMAT_MAILBOX_LIST, "Mailbox List" },
295   { FORMAT_ADDRESS_LIST, "Address List" },
296   { FORMAT_SIO_LABEL,    "SIO-Label"    },
297   { 0, NULL }
298 };
299 
300 static const value_string add_to_col_info[] = {
301   { 0, "No"  },
302   { 1, "Yes" },
303   { 0, NULL }
304 };
305 
306 typedef struct _header_field_t {
307   gchar *header_name;
308   gchar *description;
309   guint  header_format;
310   guint  add_to_col_info;
311 } header_field_t;
312 
313 static header_field_t *header_fields;
314 static guint num_header_fields;
315 
316 static GHashTable *custom_field_table;
317 static hf_register_info *dynamic_hf;
318 static guint dynamic_hf_size;
319 
320 static gboolean
header_fields_update_cb(void * r,char ** err)321 header_fields_update_cb(void *r, char **err)
322 {
323   header_field_t *rec = (header_field_t *)r;
324   char c;
325 
326   if (rec->header_name == NULL) {
327     *err = g_strdup("Header name can't be empty");
328     return FALSE;
329   }
330 
331   g_strstrip(rec->header_name);
332   if (rec->header_name[0] == 0) {
333     *err = g_strdup("Header name can't be empty");
334     return FALSE;
335   }
336 
337   /* Check for invalid characters (to avoid asserting out when
338    * registering the field).
339    */
340   c = proto_check_field_name(rec->header_name);
341   if (c) {
342     *err = g_strdup_printf("Header name can't contain '%c'", c);
343     return FALSE;
344   }
345 
346   *err = NULL;
347   return TRUE;
348 }
349 
350 static void *
header_fields_copy_cb(void * n,const void * o,size_t siz _U_)351 header_fields_copy_cb(void *n, const void *o, size_t siz _U_)
352 {
353   header_field_t *new_rec = (header_field_t *)n;
354   const header_field_t *old_rec = (const header_field_t *)o;
355 
356   new_rec->header_name = g_strdup(old_rec->header_name);
357   new_rec->description = g_strdup(old_rec->description);
358   new_rec->header_format = old_rec->header_format;
359   new_rec->add_to_col_info = old_rec->add_to_col_info;
360 
361   return new_rec;
362 }
363 
364 static void
header_fields_free_cb(void * r)365 header_fields_free_cb(void *r)
366 {
367   header_field_t *rec = (header_field_t *)r;
368 
369   g_free(rec->header_name);
370   g_free(rec->description);
371 }
372 
373 UAT_CSTRING_CB_DEF(header_fields, header_name, header_field_t)
374 UAT_CSTRING_CB_DEF(header_fields, description, header_field_t)
375 UAT_VS_DEF(header_fields, header_format, header_field_t, guint, 0, "Unstructured")
376 UAT_VS_DEF(header_fields, add_to_col_info, header_field_t, guint, 0, "No")
377 
378 
379 /* Define media_type/Content type table */
380 static dissector_table_t media_type_dissector_table;
381 
382 static void
dissect_imf_address(tvbuff_t * tvb,int offset,int length,proto_item * item,packet_info * pinfo)383 dissect_imf_address(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo)
384 {
385   proto_tree *group_tree;
386   proto_item *group_item;
387   int addr_pos;
388 
389   /* if there is a colon present it is a group */
390   if((addr_pos = tvb_find_guint8(tvb, offset, length, ':')) == -1) {
391 
392     /* there isn't - so it must be a mailbox */
393     dissect_imf_mailbox(tvb, offset, length, item, pinfo);
394 
395   } else {
396 
397     /* it is a group */
398     group_tree = proto_item_add_subtree(item, ett_imf_group);
399 
400     /* the display-name is mandatory */
401     group_item = proto_tree_add_item(group_tree, hf_imf_display_name, tvb, offset, addr_pos - offset - 1, ENC_ASCII|ENC_NA);
402 
403     /* consume any whitespace */
404     for(addr_pos++ ;addr_pos < (offset + length); addr_pos++) {
405       if(!g_ascii_isspace(tvb_get_guint8(tvb, addr_pos))) {
406         break;
407       }
408     }
409 
410     if(tvb_get_guint8(tvb, addr_pos) != ';') {
411 
412       dissect_imf_mailbox_list(tvb, addr_pos, length - (addr_pos - offset), group_item, pinfo);
413 
414       /* XXX: need to check for final ';' */
415 
416     }
417 
418   }
419 }
420 
421 static void
dissect_imf_mailbox(tvbuff_t * tvb,int offset,int length,proto_item * item,packet_info * pinfo _U_)422 dissect_imf_mailbox(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo _U_)
423 {
424   proto_tree *mbox_tree;
425   int        addr_pos, end_pos;
426 
427   mbox_tree = proto_item_add_subtree(item, ett_imf_mailbox);
428 
429   /* Here is the plan:
430      If we can't find and angle brackets, then the whole field is an address.
431      If we find angle brackets, then the address is between them and the display name is
432      anything before the opening angle bracket
433   */
434 
435   if((addr_pos = tvb_find_guint8(tvb, offset, length, '<')) == -1) {
436     /* we can't find an angle bracket - the whole field is therefore the address */
437 
438     (void) proto_tree_add_item(mbox_tree, hf_imf_address, tvb, offset, length, ENC_ASCII|ENC_NA);
439 
440   } else {
441     /* we can find an angle bracket - let's see if we can find a display name */
442     /* XXX: the '<' could be in the display name */
443 
444     for(; offset < addr_pos; offset++) {
445       if(!g_ascii_isspace(tvb_get_guint8(tvb, offset))) {
446         break;
447       }
448     }
449 
450     if(offset != addr_pos) { /* there is a display name */
451       (void) proto_tree_add_item(mbox_tree, hf_imf_display_name, tvb, offset, addr_pos - offset - 1, ENC_ASCII|ENC_NA);
452     }
453     end_pos = tvb_find_guint8(tvb, addr_pos + 1, length - (addr_pos + 1 - offset), '>');
454 
455     if(end_pos != -1) {
456       (void) proto_tree_add_item(mbox_tree, hf_imf_address, tvb, addr_pos + 1, end_pos - addr_pos - 1, ENC_ASCII|ENC_NA);
457     }
458   }
459 }
460 
461 static void
dissect_imf_address_list(tvbuff_t * tvb,int offset,int length,proto_item * item,packet_info * pinfo)462 dissect_imf_address_list(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo)
463 {
464   proto_item *addr_item = NULL;
465   proto_tree *tree = NULL;
466   int         count = 0;
467   int         item_offset;
468   int         end_offset;
469   int         item_length;
470 
471   /* a comma separated list of addresses */
472   tree = proto_item_add_subtree(item, ett_imf_address_list);
473 
474   item_offset = offset;
475 
476   do {
477 
478     end_offset = tvb_find_guint8(tvb, item_offset, length - (item_offset - offset), ',');
479 
480     count++; /* increase the number of items */
481 
482     if(end_offset == -1) {
483       /* length is to the end of the buffer */
484       item_length = length - (item_offset - offset);
485     } else {
486       item_length = end_offset - item_offset;
487     }
488     addr_item = proto_tree_add_item(tree, hf_imf_address_list_item, tvb, item_offset, item_length, ENC_ASCII|ENC_NA);
489     dissect_imf_address(tvb, item_offset, item_length, addr_item, pinfo);
490 
491     if(end_offset != -1) {
492       item_offset = end_offset + 1;
493     }
494   } while(end_offset != -1);
495 
496   /* now indicate the number of items found */
497   proto_item_append_text(item, ", %d item%s", count, plurality(count, "", "s"));
498 }
499 
500 static void
dissect_imf_mailbox_list(tvbuff_t * tvb,int offset,int length,proto_item * item,packet_info * pinfo)501 dissect_imf_mailbox_list(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo)
502 {
503   proto_item *mbox_item = NULL;
504   proto_tree *tree = NULL;
505   int         count = 0;
506   int         item_offset;
507   int         end_offset;
508   int         item_length;
509 
510   /* a comma separated list of mailboxes */
511   tree = proto_item_add_subtree(item, ett_imf_mailbox_list);
512 
513   item_offset = offset;
514 
515   do {
516 
517     end_offset = tvb_find_guint8(tvb, item_offset, length - (item_offset - offset), ',');
518 
519     count++; /* increase the number of items */
520 
521     if(end_offset == -1) {
522       /* length is to the end of the buffer */
523       item_length = length - (item_offset - offset);
524     } else {
525       item_length = end_offset - item_offset;
526     }
527     mbox_item = proto_tree_add_item(tree, hf_imf_mailbox_list_item, tvb, item_offset, item_length, ENC_ASCII|ENC_NA);
528     dissect_imf_mailbox(tvb, item_offset, item_length, mbox_item, pinfo);
529 
530     if(end_offset != -1) {
531       item_offset = end_offset + 1;
532     }
533   } while(end_offset != -1);
534 
535   /* now indicate the number of items found */
536   proto_item_append_text(item, ", %d item%s", count, plurality(count, "", "s"));
537 }
538 
539 static void
dissect_imf_siolabel(tvbuff_t * tvb,int offset,int length,proto_item * item,packet_info * pinfo)540 dissect_imf_siolabel(tvbuff_t *tvb, int offset, int length, proto_item *item, packet_info *pinfo)
541 {
542   proto_tree *tree = NULL;
543   proto_item *sub_item = NULL;
544   int         item_offset, item_length;
545   int         value_offset, value_length;
546   int         end_offset;
547   tvbuff_t   *label_tvb;
548   gchar      *type = NULL;
549   wmem_strbuf_t  *label_string = wmem_strbuf_new(pinfo->pool, "");
550 
551   /* a semicolon separated list of attributes */
552   tree = proto_item_add_subtree(item, ett_imf_siolabel);
553   item_offset = offset;
554 
555   do {
556     end_offset = tvb_find_guint8(tvb, item_offset, length - (item_offset - offset), ';');
557 
558     /* skip leading space */
559     while (g_ascii_isspace(tvb_get_guint8(tvb, item_offset))) {
560       item_offset++;
561     }
562 
563     if (end_offset == -1) {
564       /* length is to the end of the buffer */
565       item_length = tvb_find_line_end(tvb, item_offset, length - (item_offset - offset), NULL, FALSE);
566     } else {
567       item_length = end_offset - item_offset;
568     }
569 
570     value_offset = tvb_find_guint8(tvb, item_offset, length - (item_offset - offset), '=') + 1;
571     while (g_ascii_isspace(tvb_get_guint8(tvb, value_offset))) {
572       value_offset++;
573     }
574 
575     value_length = item_length - (value_offset - item_offset);
576     while (g_ascii_isspace(tvb_get_guint8(tvb, value_offset + value_length - 1))) {
577       value_length--;
578     }
579 
580     if (tvb_strneql(tvb, item_offset, "marking", 7) == 0) {
581       const guint8* marking;
582       proto_tree_add_item_ret_string(tree, hf_imf_siolabel_marking, tvb, value_offset, value_length, ENC_ASCII|ENC_NA, pinfo->pool, &marking);
583       proto_item_append_text(item, ": %s", marking);
584 
585     } else if (tvb_strneql(tvb, item_offset, "fgcolor", 7) == 0) {
586       proto_tree_add_item(tree, hf_imf_siolabel_fgcolor, tvb, value_offset, value_length, ENC_ASCII|ENC_NA);
587 
588     } else if (tvb_strneql(tvb, item_offset, "bgcolor", 7) == 0) {
589       proto_tree_add_item(tree, hf_imf_siolabel_bgcolor, tvb, value_offset, value_length, ENC_ASCII|ENC_NA);
590 
591     } else if (tvb_strneql(tvb, item_offset, "type", 4) == 0) {
592       type = tvb_get_string_enc(pinfo->pool, tvb, value_offset + 1, value_length - 2, ENC_ASCII); /* quoted */
593       proto_tree_add_item(tree, hf_imf_siolabel_type, tvb, value_offset, value_length, ENC_ASCII|ENC_NA);
594 
595     } else if (tvb_strneql(tvb, item_offset, "label", 5) == 0) {
596       gchar *label = tvb_get_string_enc(pinfo->pool, tvb, value_offset + 1, value_length - 2, ENC_ASCII); /* quoted */
597       wmem_strbuf_append(label_string, label);
598 
599       if (tvb_get_guint8(tvb, item_offset + 5) == '*') { /* continuations */
600         int num = (int)strtol(tvb_get_string_enc(pinfo->pool, tvb, item_offset + 6, value_offset - item_offset + 6, ENC_ASCII), NULL, 10);
601         proto_tree_add_string_format(tree, hf_imf_siolabel_label, tvb, value_offset, value_length,
602                                      label, "Label[%d]: \"%s\"", num, label);
603       } else {
604         proto_tree_add_item(tree, hf_imf_siolabel_label, tvb, value_offset, value_length, ENC_ASCII|ENC_NA);
605       }
606 
607     } else {
608       sub_item = proto_tree_add_item(tree, hf_imf_siolabel_unknown, tvb, item_offset, item_length, ENC_ASCII|ENC_NA);
609       expert_add_info(pinfo, sub_item, &ei_imf_unknown_param);
610     }
611 
612     if (end_offset != -1) {
613       item_offset = end_offset + 1;
614     }
615   } while (end_offset != -1);
616 
617   if (type && wmem_strbuf_get_len(label_string) > 0) {
618     if (strcmp (type, ":ess") == 0) {
619       label_tvb = base64_to_tvb(tvb, wmem_strbuf_get_str(label_string));
620       add_new_data_source(pinfo, label_tvb, "ESS Security Label");
621       dissect_ess_ESSSecurityLabel_PDU(label_tvb, pinfo, tree, NULL);
622     } else if (strcmp (type, ":x411") == 0) {
623       label_tvb = base64_to_tvb(tvb, wmem_strbuf_get_str(label_string));
624       add_new_data_source(pinfo, label_tvb, "X.411 Security Label");
625       dissect_p1_MessageSecurityLabel_PDU(label_tvb, pinfo, tree, NULL);
626     }
627   }
628 }
629 
630 static void
dissect_imf_content_type(tvbuff_t * tvb,packet_info * pinfo,int offset,int length,proto_item * item,const guint8 ** type,const guint8 ** parameters)631 dissect_imf_content_type(tvbuff_t *tvb, packet_info *pinfo, int offset, int length, proto_item *item,
632                          const guint8 **type, const guint8 **parameters)
633 {
634   int first_colon;
635   int end_offset;
636   int len;
637   int i;
638   proto_tree *ct_tree;
639 
640   /* first strip any whitespace */
641   for(i = 0; i < length; i++) {
642     if(!g_ascii_isspace(tvb_get_guint8(tvb, offset + i))) {
643       offset += i;
644       break;
645     }
646   }
647 
648   /* find the first colon - there has to be a colon as there will have to be a boundary */
649   first_colon = tvb_find_guint8(tvb, offset, length, ';');
650 
651   if(first_colon != -1) {
652     ct_tree = proto_item_add_subtree(item, ett_imf_content_type);
653 
654     len = first_colon - offset;
655     proto_tree_add_item_ret_string(ct_tree, hf_imf_content_type_type, tvb, offset, len, ENC_ASCII|ENC_NA, pinfo->pool, type);
656     end_offset = imf_find_field_end (tvb, first_colon + 1, offset + length, NULL);
657     if (end_offset == -1) {
658        /* No end found */
659        return;
660     }
661     len = end_offset - (first_colon + 1) - 2;  /* Do not include the last CRLF */
662     proto_tree_add_item_ret_string(ct_tree, hf_imf_content_type_parameters, tvb, first_colon + 1, len, ENC_ASCII|ENC_NA, pinfo->pool, parameters);
663   }
664 }
665 
666 
667 int
imf_find_field_end(tvbuff_t * tvb,int offset,gint max_length,gboolean * last_field)668 imf_find_field_end(tvbuff_t *tvb, int offset, gint max_length, gboolean *last_field)
669 {
670 
671   while(offset < max_length) {
672 
673     /* look for CR */
674     offset = tvb_find_guint8(tvb, offset, max_length - offset, '\r');
675 
676     if(offset != -1) {
677       /* protect against buffer overrun and only then look for next char */
678         if (++offset < max_length && tvb_get_guint8(tvb, offset) == '\n') {
679         /* OK - so we have found CRLF */
680           if (++offset >= max_length) {
681             /* end of buffer and also end of fields */
682             if (last_field) {
683               *last_field = TRUE;
684             }
685             /* caller expects that there is CRLF after returned offset, if last_field is set */
686             return offset - 2;
687           }
688         /* peek the next character */
689         switch(tvb_get_guint8(tvb, offset)) {
690         case '\r':
691           /* probably end of the fields */
692           if ((offset + 1) < max_length && tvb_get_guint8(tvb, offset + 1) == '\n') {
693             if(last_field) {
694               *last_field = TRUE;
695             }
696           }
697           return offset;
698         case  ' ':
699         case '\t':
700           /* continuation line */
701           break;
702         default:
703           /* this is a new field */
704           return offset;
705         }
706       }
707     } else {
708       /* couldn't find a CR - strange */
709       break;
710     }
711 
712   }
713 
714   return -1;  /* Fail: No CR found (other than possible continuation) */
715 
716 }
717 
718 static int
dissect_imf(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)719 dissect_imf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
720 {
721   proto_item  *item;
722   proto_tree  *unknown_tree, *text_tree;
723   const guint8 *content_type_str = NULL;
724   char  *content_encoding_str = NULL;
725   const guint8 *parameters = NULL;
726   int   hf_id;
727   gint  start_offset = 0;
728   gint  value_offset = 0;
729   gint  unknown_offset = 0;
730   gint  end_offset = 0;
731   gint   max_length;
732   guint8 *key;
733   gboolean last_field = FALSE;
734   tvbuff_t *next_tvb;
735   struct imf_field *f_info;
736   imf_eo_t *eo_info = NULL;
737 
738   if (have_tap_listener(imf_eo_tap)) {
739     eo_info = wmem_new(pinfo->pool, imf_eo_t);
740     /* initialize the eo_info fields in case they are missing later */
741     eo_info->sender_data = "";
742     eo_info->subject_data = "";
743   }
744 
745   /* Want to preserve existing protocol name and show that it is carrying IMF */
746   col_append_str(pinfo->cinfo, COL_PROTOCOL, "/");
747   col_set_fence(pinfo->cinfo, COL_PROTOCOL);
748   col_set_str(pinfo->cinfo, COL_PROTOCOL, PSNAME);
749 
750   col_clear(pinfo->cinfo, COL_INFO);
751 
752   item = proto_tree_add_item(tree, proto_imf, tvb, 0, -1, ENC_NA);
753   tree = proto_item_add_subtree(item, ett_imf);
754 
755   max_length = tvb_captured_length(tvb);
756   /* first go through the tvb until we find a blank line and extract the content type if
757      we find one */
758 
759   /* XXX: What if the tvb contains encrypted data ? is there a way to bypass dissection if so ?  */
760   /*      As it is, the following code blithely tries to parse what may be binary data.          */
761 
762   while(!last_field) {
763 
764     /* look for a colon first */
765     end_offset = tvb_find_guint8(tvb, start_offset, max_length - start_offset, ':');
766 
767     if(end_offset == -1) {
768       /* we couldn't find another colon - strange - we should have broken out of here by now */
769       /* XXX: flag an error */
770       break;
771     } else {
772       key = tvb_get_string_enc(pinfo->pool, tvb, start_offset, end_offset - start_offset, ENC_ASCII);
773 
774       /* convert to lower case */
775       ascii_strdown_inplace (key);
776 
777       /* look up the key in built-in fields */
778       f_info = (struct imf_field *)wmem_map_lookup(imf_field_table, key);
779 
780       if(f_info == NULL && custom_field_table) {
781         /* look up the key in custom fields */
782         f_info = (struct imf_field *)g_hash_table_lookup(custom_field_table, key);
783       }
784 
785       if(f_info == NULL) {
786         /* set as an unknown extension */
787         f_info = imf_fields;
788         unknown_offset = start_offset;
789       }
790 
791       hf_id = *(f_info->hf_id);
792 
793       /* value starts immediately after the colon */
794       start_offset = end_offset+1;
795 
796       end_offset = imf_find_field_end(tvb, start_offset, max_length, &last_field);
797       if(end_offset == -1) {
798         break;   /* Something's fishy */
799       }
800 
801       /* remove any leading whitespace */
802 
803       for(value_offset = start_offset; value_offset < end_offset; value_offset++)
804         if(!g_ascii_isspace(tvb_get_guint8(tvb, value_offset))) {
805           break;
806         }
807 
808       if(value_offset == end_offset) {
809         /* empty field - show whole value */
810         value_offset = start_offset;
811       }
812 
813       if(hf_id == hf_imf_extension_type) {
814 
815         /* remove 2 bytes to take off the final CRLF to make things a little prettier */
816         item = proto_tree_add_item(tree, hf_imf_extension, tvb, unknown_offset, end_offset - unknown_offset - 2, ENC_ASCII|ENC_NA);
817 
818         proto_item_append_text(item, " (Contact Wireshark developers if you want this supported.)");
819 
820         unknown_tree = proto_item_add_subtree(item, ett_imf_extension);
821 
822         proto_tree_add_item(unknown_tree, hf_imf_extension_type, tvb, unknown_offset, start_offset - 1 - unknown_offset, ENC_ASCII|ENC_NA);
823 
824         /* remove 2 bytes to take off the final CRLF to make things a little prettier */
825         item = proto_tree_add_item(unknown_tree, hf_imf_extension_value, tvb, value_offset, end_offset - value_offset - 2, ENC_ASCII|ENC_NA);
826 
827       } else {
828         /* remove 2 bytes to take off the final CRLF to make things a little prettier */
829         item = proto_tree_add_item(tree, hf_id, tvb, value_offset, end_offset - value_offset - 2, ENC_ASCII|ENC_NA);
830       }
831       if(f_info->add_to_col_info) {
832 
833         col_append_fstr(pinfo->cinfo, COL_INFO, "%s: %s, ", f_info->name,
834                         tvb_format_text(pinfo->pool, tvb, value_offset, end_offset - value_offset - 2));
835 
836         /* if sender or subject, store for sending to the tap */
837         if (eo_info && have_tap_listener(imf_eo_tap)) {
838           if (*f_info->hf_id == hf_imf_from) {
839             eo_info->sender_data = tvb_get_string_enc(pinfo->pool, tvb, value_offset, end_offset - value_offset - 2, ENC_ASCII|ENC_NA);
840           } else if(*f_info->hf_id == hf_imf_subject) {
841             eo_info->subject_data = tvb_get_string_enc(pinfo->pool, tvb, value_offset, end_offset - value_offset - 2, ENC_ASCII|ENC_NA);
842           }
843         }
844       }
845 
846       if(hf_id == hf_imf_content_type) {
847         /* we need some additional processing to extract the content type and parameters */
848 
849         dissect_imf_content_type(tvb, pinfo, start_offset, end_offset - start_offset, item,
850                                  &content_type_str, &parameters);
851 
852       } else if (hf_id == hf_imf_content_transfer_encoding) {
853         content_encoding_str = tvb_get_string_enc (pinfo->pool, tvb, value_offset, end_offset - value_offset - 2, ENC_ASCII);
854       } else if(f_info->subdissector) {
855 
856         /* we have a subdissector */
857         f_info->subdissector(tvb, value_offset, end_offset - value_offset, item, pinfo);
858 
859       }
860     }
861     start_offset = end_offset;
862   }
863 
864   if (last_field) {
865     /* Remove the extra CRLF after all the fields */
866     end_offset += 2;
867   }
868 
869   if (end_offset == -1) {
870     end_offset = 0;
871   }
872 
873   /* specify a content type until we can work it out for ourselves */
874   /* content_type_str = "multipart/mixed"; */
875 
876   /* now dissect the MIME based upon the content type */
877 
878   if(content_type_str && media_type_dissector_table) {
879     http_message_info_t message_info;
880 
881     col_set_fence(pinfo->cinfo, COL_INFO);
882 
883     if(content_encoding_str && !g_ascii_strncasecmp(content_encoding_str, "base64", 6)) {
884       char *string_data = tvb_get_string_enc(pinfo->pool, tvb, end_offset, tvb_reported_length(tvb) - end_offset, ENC_ASCII);
885       next_tvb = base64_to_tvb(tvb, string_data);
886       add_new_data_source(pinfo, next_tvb, content_encoding_str);
887     } else {
888       next_tvb = tvb_new_subset_remaining(tvb, end_offset);
889     }
890 
891     message_info.type = HTTP_OTHERS;
892     message_info.media_str = parameters;
893     message_info.data = NULL;
894     dissector_try_string(media_type_dissector_table, content_type_str, next_tvb, pinfo, tree, (void*)&message_info);
895   } else {
896 
897     /* just show the lines or highlight the rest of the buffer as message text */
898 
899     item = proto_tree_add_item(tree, hf_imf_message_text, tvb, end_offset, tvb_reported_length_remaining(tvb, end_offset) , ENC_NA);
900     text_tree = proto_item_add_subtree(item, ett_imf_message_text);
901 
902     start_offset = end_offset;
903     while (tvb_offset_exists(tvb, start_offset)) {
904 
905       /*
906        * Find the end of the line.
907        */
908       tvb_find_line_end(tvb, start_offset, -1, &end_offset, FALSE);
909 
910       /*
911        * Put this line.
912        */
913       proto_tree_add_format_wsp_text(text_tree, tvb, start_offset, end_offset - start_offset);
914       col_append_sep_str(pinfo->cinfo, COL_INFO, ", ",
915                          tvb_format_text_wsp(pinfo->pool, tvb, start_offset, end_offset - start_offset));
916 
917       /*
918        * Step to the next line.
919        */
920       start_offset = end_offset;
921     }
922   }
923 
924   if (eo_info && have_tap_listener(imf_eo_tap)) {
925     /* Set payload info */
926     eo_info->payload_len = max_length;
927     eo_info->payload_data = (gchar *) tvb_memdup(pinfo->pool, tvb, 0, max_length);
928 
929     /* Send to tap */
930     tap_queue_packet(imf_eo_tap, pinfo, eo_info);
931   }
932   return tvb_captured_length(tvb);
933 }
934 
935 static void
free_imf_field(gpointer data)936 free_imf_field (gpointer data)
937 {
938   struct imf_field *imffield = (struct imf_field *) data;
939 
940   g_free (imffield->name);
941   g_free (imffield);
942 }
943 
944 static void
deregister_header_fields(void)945 deregister_header_fields(void)
946 {
947   if (dynamic_hf) {
948     /* Deregister all fields */
949     for (guint i = 0; i < dynamic_hf_size; i++) {
950       proto_deregister_field (proto_imf, *(dynamic_hf[i].p_id));
951       g_free (dynamic_hf[i].p_id);
952     }
953 
954     proto_add_deregistered_data (dynamic_hf);
955     dynamic_hf = NULL;
956     dynamic_hf_size = 0;
957   }
958 
959   if (custom_field_table) {
960     g_hash_table_destroy (custom_field_table);
961     custom_field_table = NULL;
962   }
963 }
964 
965 static void
header_fields_post_update_cb(void)966 header_fields_post_update_cb (void)
967 {
968   gint *hf_id;
969   struct imf_field *imffield;
970   gchar *header_name;
971 
972   deregister_header_fields();
973 
974   if (num_header_fields) {
975     custom_field_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_imf_field);
976     dynamic_hf = g_new0(hf_register_info, num_header_fields);
977     dynamic_hf_size = num_header_fields;
978 
979     for (guint i = 0; i < dynamic_hf_size; i++) {
980       hf_id = g_new(gint, 1);
981       *hf_id = -1;
982       header_name = g_strdup (header_fields[i].header_name);
983 
984       dynamic_hf[i].p_id = hf_id;
985       dynamic_hf[i].hfinfo.name = header_name;
986       dynamic_hf[i].hfinfo.abbrev = g_strdup_printf ("imf.header.%s", header_name);
987       dynamic_hf[i].hfinfo.type = FT_STRING;
988       dynamic_hf[i].hfinfo.display = BASE_NONE;
989       dynamic_hf[i].hfinfo.strings = NULL;
990       dynamic_hf[i].hfinfo.bitmask = 0;
991       dynamic_hf[i].hfinfo.blurb = g_strdup (header_fields[i].description);
992       HFILL_INIT(dynamic_hf[i]);
993 
994       imffield = g_new(struct imf_field, 1);
995       imffield->hf_id = hf_id;
996       imffield->name = g_ascii_strdown(header_name, -1);
997       switch (header_fields[i].header_format) {
998       case FORMAT_UNSTRUCTURED:
999         imffield->subdissector = NO_SUBDISSECTION;
1000         break;
1001       case FORMAT_MAILBOX:
1002         imffield->subdissector = dissect_imf_mailbox;
1003         break;
1004       case FORMAT_ADDRESS:
1005         imffield->subdissector = dissect_imf_address;
1006         break;
1007       case FORMAT_MAILBOX_LIST:
1008         imffield->subdissector = dissect_imf_mailbox_list;
1009         break;
1010       case FORMAT_ADDRESS_LIST:
1011         imffield->subdissector = dissect_imf_address_list;
1012         break;
1013       case FORMAT_SIO_LABEL:
1014         dynamic_hf[i].hfinfo.type = FT_NONE; /* constructed */
1015         imffield->subdissector = dissect_imf_siolabel;
1016         break;
1017       default:
1018         /* unknown */
1019         imffield->subdissector = NO_SUBDISSECTION;
1020         break;
1021       }
1022       imffield->add_to_col_info = header_fields[i].add_to_col_info;
1023       g_hash_table_insert (custom_field_table, (gpointer)imffield->name, (gpointer)imffield);
1024     }
1025 
1026     proto_register_field_array (proto_imf, dynamic_hf, dynamic_hf_size);
1027   }
1028 }
1029 
1030 static void
header_fields_reset_cb(void)1031 header_fields_reset_cb(void)
1032 {
1033   deregister_header_fields();
1034 }
1035 
1036 /* Register all the bits needed by the filtering engine */
1037 
1038 void
proto_register_imf(void)1039 proto_register_imf(void)
1040 {
1041   static hf_register_info hf[] = {
1042     { &hf_imf_date,
1043       { "Date", "imf.date", FT_STRING,  BASE_NONE, NULL, 0x0,
1044         "DateTime", HFILL }},
1045     { &hf_imf_from,
1046       { "From", "imf.from", FT_STRING,  BASE_NONE, NULL, 0x0,
1047         "MailboxList", HFILL }},
1048     { &hf_imf_sender,
1049       { "Sender", "imf.sender", FT_STRING,  BASE_NONE, NULL, 0x0,
1050         NULL, HFILL }},
1051     { &hf_imf_reply_to,
1052       { "Reply-To", "imf.reply_to", FT_STRING,  BASE_NONE, NULL, 0x0,
1053         NULL, HFILL }},
1054     { &hf_imf_to,
1055       { "To", "imf.to", FT_STRING,  BASE_NONE, NULL, 0x0,
1056         NULL, HFILL }},
1057     { &hf_imf_cc,
1058       { "Cc", "imf.cc", FT_STRING,  BASE_NONE, NULL, 0x0,
1059         NULL, HFILL }},
1060     { &hf_imf_bcc,
1061       { "Bcc", "imf.bcc", FT_STRING,  BASE_NONE, NULL, 0x0,
1062         NULL, HFILL }},
1063     { &hf_imf_message_id,
1064       { "Message-ID", "imf.message_id", FT_STRING,  BASE_NONE, NULL, 0x0,
1065         NULL, HFILL }},
1066     { &hf_imf_in_reply_to,
1067       { "In-Reply-To", "imf.in_reply_to", FT_STRING,  BASE_NONE, NULL, 0x0,
1068         NULL, HFILL }},
1069     { &hf_imf_references,
1070       { "References", "imf.references", FT_STRING,  BASE_NONE, NULL, 0x0,
1071         NULL, HFILL }},
1072     { &hf_imf_subject,
1073       { "Subject", "imf.subject", FT_STRING,  BASE_NONE, NULL, 0x0,
1074         NULL, HFILL }},
1075     { &hf_imf_comments,
1076       { "Comments", "imf.comments", FT_STRING,  BASE_NONE, NULL, 0x0,
1077         NULL, HFILL }},
1078     { &hf_imf_user_agent,
1079       { "User-Agent", "imf.user_agent", FT_STRING,  BASE_NONE, NULL, 0x0,
1080         NULL, HFILL }},
1081     { &hf_imf_keywords,
1082       { "Keywords", "imf.keywords", FT_STRING,  BASE_NONE, NULL, 0x0,
1083         NULL, HFILL }},
1084     { &hf_imf_resent_date,
1085       { "Resent-Date", "imf.resent.date", FT_STRING,  BASE_NONE, NULL, 0x0,
1086         NULL, HFILL }},
1087     { &hf_imf_resent_from,
1088       { "Resent-From", "imf.resent.from", FT_STRING,  BASE_NONE, NULL, 0x0,
1089         NULL, HFILL }},
1090     { &hf_imf_resent_sender,
1091       { "Resent-Sender", "imf.resent.sender", FT_STRING,  BASE_NONE, NULL, 0x0,
1092         NULL, HFILL }},
1093     { &hf_imf_resent_to,
1094       { "Resent-To", "imf.resent.to", FT_STRING,  BASE_NONE, NULL, 0x0,
1095         NULL, HFILL }},
1096     { &hf_imf_resent_cc,
1097       { "Resent-Cc", "imf.resent.cc", FT_STRING,  BASE_NONE, NULL, 0x0,
1098         NULL, HFILL }},
1099     { &hf_imf_resent_bcc,
1100       { "Resent-Bcc", "imf.resent.bcc", FT_STRING,  BASE_NONE, NULL, 0x0,
1101         NULL, HFILL }},
1102     { &hf_imf_resent_message_id,
1103       { "Resent-Message-ID", "imf.resent.message_id", FT_STRING,  BASE_NONE, NULL, 0x0,
1104         NULL, HFILL }},
1105     { &hf_imf_return_path,
1106       { "Return-Path", "imf.return_path", FT_STRING,  BASE_NONE, NULL, 0x0,
1107         NULL, HFILL }},
1108     { &hf_imf_received,
1109       { "Received", "imf.received", FT_STRING,  BASE_NONE, NULL, 0x0,
1110         NULL, HFILL }},
1111     { &hf_imf_content_type,
1112       { "Content-Type", "imf.content.type", FT_STRING,  BASE_NONE, NULL, 0x0,
1113         NULL, HFILL }},
1114     { &hf_imf_content_type_type,
1115       { "Type", "imf.content.type.type", FT_STRING,  BASE_NONE, NULL, 0x0,
1116         NULL, HFILL }},
1117     { &hf_imf_content_type_parameters,
1118       { "Parameters", "imf.content.type.parameters", FT_STRING,  BASE_NONE, NULL, 0x0,
1119         NULL, HFILL }},
1120     { &hf_imf_content_description,
1121       { "Content-Description", "imf.content.description", FT_STRING,  BASE_NONE, NULL, 0x0,
1122         NULL, HFILL }},
1123     { &hf_imf_content_id,
1124       { "Content-ID", "imf.content.id", FT_STRING,  BASE_NONE, NULL, 0x0,
1125         NULL, HFILL }},
1126     { &hf_imf_content_transfer_encoding,
1127       { "Content-Transfer-Encoding", "imf.content.transfer_encoding", FT_STRING,  BASE_NONE, NULL, 0x0,
1128         NULL, HFILL }},
1129     { &hf_imf_mime_version,
1130       { "MIME-Version", "imf.mime_version", FT_STRING,  BASE_NONE, NULL, 0x0,
1131         NULL, HFILL }},
1132     { &hf_imf_autoforwarded,
1133       { "Autoforwarded", "imf.autoforwarded", FT_STRING, BASE_NONE, NULL, 0x0,
1134         NULL, HFILL }},
1135     { &hf_imf_autosubmitted,
1136       { "Autosubmitted", "imf.autosubmitted", FT_STRING, BASE_NONE, NULL, 0x0,
1137         NULL, HFILL }},
1138     { &hf_imf_x400_content_identifier,
1139       { "X400-Content-Identifier", "imf.x400_content_identifier", FT_STRING, BASE_NONE, NULL, 0x0,
1140         NULL, HFILL }},
1141     { &hf_imf_content_language,
1142       { "Content-Language", "imf.content_language", FT_STRING, BASE_NONE, NULL, 0x0,
1143         NULL, HFILL }},
1144     { &hf_imf_conversion,
1145         { "Conversion", "imf.conversion", FT_STRING, BASE_NONE, NULL, 0x0,
1146           NULL, HFILL }},
1147     { &hf_imf_conversion_with_loss,
1148         { "Conversion-With-Loss", "imf.conversion_with_loss", FT_STRING, BASE_NONE, NULL, 0x0,
1149           NULL, HFILL }},
1150     { &hf_imf_delivery_date,
1151         { "Delivery-Date", "imf.delivery_date", FT_STRING, BASE_NONE, NULL, 0x0,
1152           NULL, HFILL }},
1153     { &hf_imf_discarded_x400_ipms_extensions,
1154         { "Discarded-X400-IPMS-Extensions", "imf.discarded_x400_ipms_extensions", FT_STRING, BASE_NONE, NULL, 0x0,
1155           NULL, HFILL }},
1156     { &hf_imf_discarded_x400_mts_extensions,
1157       { "Discarded-X400-MTS-Extensions", "imf.discarded_x400_mts_extensions", FT_STRING, BASE_NONE, NULL, 0x0,
1158         NULL, HFILL }},
1159     { &hf_imf_dl_expansion_history,
1160         { "DL-Expansion-History", "imf.dl_expansion_history", FT_STRING, BASE_NONE, NULL, 0x0,
1161           NULL, HFILL }},
1162     { &hf_imf_deferred_delivery,
1163         { "Deferred-Delivery", "imf.deferred_delivery", FT_STRING, BASE_NONE, NULL, 0x0,
1164           NULL, HFILL }},
1165     { &hf_imf_expires,
1166         { "Expires", "imf.expires", FT_STRING, BASE_NONE, NULL, 0x0,
1167           NULL, HFILL }},
1168     { &hf_imf_importance,
1169       { "Importance", "imf.importance", FT_STRING, BASE_NONE, NULL, 0x0,
1170         NULL, HFILL }},
1171     { &hf_imf_incomplete_copy,
1172         { "Incomplete-Copy", "imf.incomplete_copy", FT_STRING, BASE_NONE, NULL, 0x0,
1173           NULL, HFILL }},
1174     { &hf_imf_latest_delivery_time,
1175       { "Latest-Delivery-Time", "imf.latest_delivery_time", FT_STRING, BASE_NONE, NULL, 0x0,
1176         NULL, HFILL }},
1177     { &hf_imf_message_type,
1178         { "Message-Type", "imf.message_type", FT_STRING, BASE_NONE, NULL, 0x0,
1179           NULL, HFILL }},
1180     { &hf_imf_original_encoded_information_types,
1181         { "Original-Encoded-Information-Types", "imf.original_encoded_information_types", FT_STRING, BASE_NONE, NULL, 0x0,
1182           NULL, HFILL }},
1183     { &hf_imf_originator_return_address,
1184         { "Originator-Return-Address", "imf.originator_return_address", FT_STRING, BASE_NONE, NULL, 0x0,
1185           NULL, HFILL }},
1186     { &hf_imf_priority,
1187         { "Priority", "imf.priority", FT_STRING, BASE_NONE, NULL, 0x0,
1188           NULL, HFILL }},
1189     { &hf_imf_reply_by,
1190         { "Reply-By", "imf.reply_by", FT_STRING, BASE_NONE, NULL, 0x0,
1191           NULL, HFILL }},
1192     { &hf_imf_sensitivity,
1193         { "Sensitivity", "imf.sensitivity", FT_STRING, BASE_NONE, NULL, 0x0,
1194           NULL, HFILL }},
1195     { &hf_imf_supersedes,
1196         { "Supersedes", "imf.supersedes", FT_STRING, BASE_NONE, NULL, 0x0,
1197           NULL, HFILL }},
1198     { &hf_imf_x400_content_type,
1199         { "X400-Content-Type", "imf.x400_content_type", FT_STRING, BASE_NONE, NULL, 0x0,
1200           NULL, HFILL }},
1201     { &hf_imf_x400_mts_identifier,
1202         { "X400-MTS-Identifier", "imf.x400_mts_identifier", FT_STRING, BASE_NONE, NULL, 0x0,
1203           NULL, HFILL }},
1204     { &hf_imf_x400_originator,
1205         { "X400-Originator", "imf.x400_originator", FT_STRING, BASE_NONE, NULL, 0x0,
1206         NULL, HFILL }},
1207     { &hf_imf_x400_received,
1208         { "X400-Received", "imf.x400_received", FT_STRING, BASE_NONE, NULL, 0x0,
1209         NULL, HFILL }},
1210     { &hf_imf_x400_recipients,
1211         { "X400-Recipients", "imf.x400_recipients", FT_STRING, BASE_NONE, NULL, 0x0,
1212         NULL, HFILL }},
1213     { &hf_imf_delivered_to,
1214       { "Delivered-To", "imf.delivered_to", FT_STRING,  BASE_NONE, NULL, 0x0,
1215         NULL, HFILL }},
1216     { &hf_imf_ext_mailer,
1217       { "X-Mailer", "imf.ext.mailer", FT_STRING,  BASE_NONE, NULL, 0x0,
1218         NULL, HFILL }},
1219     { &hf_imf_ext_mimeole,
1220       { "X-MimeOLE", "imf.ext.mimeole", FT_STRING,  BASE_NONE, NULL, 0x0,
1221         NULL, HFILL }},
1222     { &hf_imf_ext_expiry_date,
1223       { "Expiry-Date", "imf.ext.expiry-date", FT_STRING,  BASE_NONE, NULL, 0x0,
1224         NULL, HFILL }},
1225     { &hf_imf_ext_tnef_correlator,
1226       { "X-MS-TNEF-Correlator", "imf.ext.tnef-correlator", FT_STRING,  BASE_NONE, NULL, 0x0,
1227         NULL, HFILL }},
1228     { &hf_imf_ext_uidl,
1229       { "X-UIDL", "imf.ext.uidl", FT_STRING,  BASE_NONE, NULL, 0x0,
1230         NULL, HFILL }},
1231     { &hf_imf_ext_authentication_warning,
1232       { "X-Authentication-Warning", "imf.ext.authentication_warning", FT_STRING,  BASE_NONE,
1233         NULL, 0x0, NULL, HFILL }},
1234     { &hf_imf_ext_virus_scanned,
1235       { "X-Virus-Scanned", "imf.ext.virus_scanned", FT_STRING,  BASE_NONE, NULL, 0x0,
1236         NULL, HFILL }},
1237     { &hf_imf_thread_index,
1238       { "Thread-Index", "imf.thread-index", FT_STRING,  BASE_NONE, NULL, 0x0,
1239         NULL, HFILL }},
1240     { &hf_imf_extension,
1241       { "Unknown-Extension", "imf.extension", FT_STRING,  BASE_NONE, NULL, 0x0,
1242         NULL, HFILL }},
1243     { &hf_imf_extension_type,
1244       { "Type", "imf.extension.type", FT_STRING,  BASE_NONE, NULL, 0x0,
1245         NULL, HFILL }},
1246     { &hf_imf_extension_value,
1247       { "Value", "imf.extension.value", FT_STRING,  BASE_NONE, NULL, 0x0,
1248         NULL, HFILL }},
1249     { &hf_imf_display_name,
1250       { "Display-Name", "imf.display_name", FT_STRING,  BASE_NONE, NULL, 0x0,
1251         NULL, HFILL }},
1252     { &hf_imf_address,
1253       { "Address", "imf.address", FT_STRING,  BASE_NONE, NULL, 0x0,
1254         NULL, HFILL }},
1255 #if 0
1256     { &hf_imf_address_list,
1257       { "Address List", "imf.address_list", FT_UINT32,  BASE_DEC, NULL, 0x0,
1258         NULL, HFILL }},
1259 #endif
1260     { &hf_imf_address_list_item,
1261       { "Item", "imf.address_list.item", FT_STRING,  BASE_NONE, NULL, 0x0,
1262         NULL, HFILL }},
1263 #if 0
1264     { &hf_imf_mailbox_list,
1265       { "Mailbox List", "imf.mailbox_list", FT_UINT32,  BASE_DEC, NULL, 0x0,
1266         NULL, HFILL }},
1267 #endif
1268     { &hf_imf_mailbox_list_item,
1269       { "Item", "imf.mailbox_list.item", FT_STRING,  BASE_NONE, NULL, 0x0,
1270         NULL, HFILL }},
1271     { &hf_imf_siolabel,
1272       { "SIO-Label", "imf.siolabel", FT_NONE,  BASE_NONE, NULL, 0x0,
1273         NULL, HFILL }},
1274     { &hf_imf_siolabel_marking,
1275       { "Marking", "imf.siolabel.marking", FT_STRING,  BASE_NONE, NULL, 0x0,
1276         NULL, HFILL }},
1277     { &hf_imf_siolabel_fgcolor,
1278       { "Foreground Color", "imf.siolabel.fgcolor", FT_STRING,  BASE_NONE, NULL, 0x0,
1279         NULL, HFILL }},
1280     { &hf_imf_siolabel_bgcolor,
1281       { "Background Color", "imf.siolabel.bgcolor", FT_STRING,  BASE_NONE, NULL, 0x0,
1282         NULL, HFILL }},
1283     { &hf_imf_siolabel_type,
1284       { "Type", "imf.siolabel.type", FT_STRING,  BASE_NONE, NULL, 0x0,
1285         NULL, HFILL }},
1286     { &hf_imf_siolabel_label,
1287       { "Label", "imf.siolabel.label", FT_STRING,  BASE_NONE, NULL, 0x0,
1288         NULL, HFILL }},
1289     { &hf_imf_siolabel_unknown,
1290       { "Unknown parameter", "imf.siolabel.unknown", FT_STRING,  BASE_NONE, NULL, 0x0,
1291         NULL, HFILL }},
1292     { &hf_imf_message_text,
1293       { "Message-Text", "imf.message_text", FT_NONE,  BASE_NONE, NULL, 0x0,
1294         NULL, HFILL }},
1295   };
1296   static gint *ett[] = {
1297     &ett_imf,
1298     &ett_imf_content_type,
1299     &ett_imf_group,
1300     &ett_imf_mailbox,
1301     &ett_imf_mailbox_list,
1302     &ett_imf_address_list,
1303     &ett_imf_siolabel,
1304     &ett_imf_extension,
1305     &ett_imf_message_text,
1306   };
1307 
1308   static ei_register_info ei[] = {
1309      { &ei_imf_unknown_param, { "imf.unknown_param", PI_PROTOCOL, PI_WARN, "Unknown parameter", EXPFILL }},
1310   };
1311 
1312   static uat_field_t attributes_flds[] = {
1313     UAT_FLD_CSTRING(header_fields, header_name, "Header name", "IMF header name"),
1314     UAT_FLD_CSTRING(header_fields, description, "Description", "Description of the value contained in the header"),
1315     UAT_FLD_VS(header_fields, header_format, "Format", header_format, 0),
1316     UAT_FLD_VS(header_fields, add_to_col_info, "Add to Info column", add_to_col_info, 0),
1317     UAT_END_FIELDS
1318   };
1319 
1320   uat_t *headers_uat = uat_new("Custom IMF headers",
1321                                sizeof(header_field_t),
1322                                "imf_header_fields",
1323                                TRUE,
1324                                &header_fields,
1325                                &num_header_fields,
1326                                /* specifies named fields, so affects dissection
1327                                   and the set of named fields */
1328                                UAT_AFFECTS_DISSECTION|UAT_AFFECTS_FIELDS,
1329                                NULL,
1330                                header_fields_copy_cb,
1331                                header_fields_update_cb,
1332                                header_fields_free_cb,
1333                                header_fields_post_update_cb,
1334                                header_fields_reset_cb,
1335                                attributes_flds);
1336 
1337   module_t *imf_module;
1338   expert_module_t* expert_imf;
1339   struct imf_field *f;
1340 
1341   proto_imf = proto_register_protocol(PNAME, PSNAME, PFNAME);
1342 
1343   proto_register_field_array(proto_imf, hf, array_length(hf));
1344   proto_register_subtree_array(ett, array_length(ett));
1345   expert_imf = expert_register_protocol(proto_imf);
1346   expert_register_field_array(expert_imf, ei, array_length(ei));
1347 
1348   /* Allow dissector to find be found by name. */
1349   imf_handle = register_dissector(PFNAME, dissect_imf, proto_imf);
1350 
1351   imf_module = prefs_register_protocol(proto_imf, NULL);
1352   prefs_register_uat_preference(imf_module, "custom_header_fields", "Custom IMF headers",
1353                                 "A table to define custom IMF headers for which fields can be "
1354                                 "setup and used for filtering/data extraction etc.",
1355                                 headers_uat);
1356 
1357   imf_field_table=wmem_map_new(wmem_epan_scope(), wmem_str_hash, g_str_equal); /* oid to syntax */
1358 
1359   /* register the fields for lookup */
1360   for(f = imf_fields; f->name; f++)
1361     wmem_map_insert(imf_field_table, (gpointer)f->name, (gpointer)f);
1362 
1363   /* Register for tapping */
1364   imf_eo_tap = register_export_object(proto_imf, imf_eo_packet, NULL);
1365 
1366 }
1367 
1368 /* The registration hand-off routine */
1369 void
proto_reg_handoff_imf(void)1370 proto_reg_handoff_imf(void)
1371 {
1372   dissector_add_string("media_type",
1373                        "message/rfc822", imf_handle);
1374 
1375   register_ber_oid_dissector_handle("1.2.840.113549.1.7.1", imf_handle, proto_imf, "id-data");
1376 
1377   /*
1378    * Get the content type and Internet media type table
1379    */
1380   media_type_dissector_table = find_dissector_table("media_type");
1381 
1382 }
1383 
1384 /*
1385  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1386  *
1387  * Local Variables:
1388  * c-basic-offset: 2
1389  * tab-width: 8
1390  * indent-tabs-mode: nil
1391  * End:
1392  *
1393  * ex: set shiftwidth=2 tabstop=8 expandtab:
1394  * :indentSize=2:tabSize=8:noTabs=true:
1395  */
1396