diff options
Diffstat (limited to 'epan')
-rw-r--r-- | epan/CMakeLists.txt | 1 | ||||
-rw-r--r-- | epan/Makefile.am | 2 | ||||
-rw-r--r-- | epan/dissectors/packet-dcm.c | 35 | ||||
-rw-r--r-- | epan/dissectors/packet-http.c | 31 | ||||
-rw-r--r-- | epan/dissectors/packet-imf.c | 32 | ||||
-rw-r--r-- | epan/dissectors/packet-smb.c | 423 | ||||
-rw-r--r-- | epan/dissectors/packet-tftp.c | 91 | ||||
-rw-r--r-- | epan/export_object.c | 206 | ||||
-rw-r--r-- | epan/export_object.h | 155 |
9 files changed, 969 insertions, 7 deletions
diff --git a/epan/CMakeLists.txt b/epan/CMakeLists.txt index 3bb415589c..f3c2f07fc4 100644 --- a/epan/CMakeLists.txt +++ b/epan/CMakeLists.txt @@ -112,6 +112,7 @@ set(LIBWIRESHARK_FILES ex-opt.c except.c expert.c + export_object.c exported_pdu.c plugin_if.c filter_expressions.c diff --git a/epan/Makefile.am b/epan/Makefile.am index 04965bb69e..de37f2fa53 100644 --- a/epan/Makefile.am +++ b/epan/Makefile.am @@ -75,6 +75,7 @@ LIBWIRESHARK_SRC = \ ex-opt.c \ except.c \ expert.c \ + export_object.c \ exported_pdu.c \ plugin_if.c \ filter_expressions.c \ @@ -218,6 +219,7 @@ LIBWIRESHARK_INCLUDES = \ except.h \ exceptions.h \ expert.h \ + export_object.h \ exported_pdu.h \ plugin_if.h \ filter_expressions.h \ diff --git a/epan/dissectors/packet-dcm.c b/epan/dissectors/packet-dcm.c index ac501bf40f..fc7ee38fbd 100644 --- a/epan/dissectors/packet-dcm.c +++ b/epan/dissectors/packet-dcm.c @@ -221,6 +221,7 @@ #include <epan/expert.h> #include <epan/tap.h> #include <epan/reassemble.h> +#include <epan/export_object.h> #include "packet-tcp.h" @@ -369,6 +370,38 @@ static const value_string dcm_assoc_item_type[] = { { 0, NULL } }; +static gboolean +dcm_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, + const void *data) +{ + export_object_list_t *object_list = (export_object_list_t *)tapdata; + const dicom_eo_t *eo_info = (const dicom_eo_t *)data; + export_object_entry_t *entry; + + if (eo_info) { /* We have data waiting for us */ + /* + Don't copy any data. dcm_export_create_object() is already g_malloc() the items + Still, the values will be freed when the export Object window is closed. + Therefore, strings and buffers must be copied + */ + entry = g_new(export_object_entry_t, 1); + + entry->pkt_num = pinfo->num; + entry->hostname = eo_info->hostname; + entry->content_type = eo_info->content_type; + entry->filename = g_path_get_basename(eo_info->filename); + entry->payload_len = eo_info->payload_len; + entry->payload_data = eo_info->payload_data; + + object_list->add_entry(object_list->gui_data, entry); + + return TRUE; /* State changed - window should be redrawn */ + } else { + return FALSE; /* State unchanged - no window updates needed */ + } +} + + /* ************************************************************************* */ /* Fragment items */ /* ************************************************************************* */ @@ -7175,7 +7208,7 @@ proto_register_dcm(void) "When not set, the decoding may fail and the exports may become corrupt.", &global_dcm_reassemble); - dicom_eo_tap = register_tap("dicom_eo"); /* DICOM Export Object tap */ + dicom_eo_tap = register_export_object(proto_dcm, dcm_eo_packet, NULL); register_init_routine(&dcm_init); register_cleanup_routine(&dcm_cleanup); diff --git a/epan/dissectors/packet-http.c b/epan/dissectors/packet-http.c index 839f562908..194e0eeec0 100644 --- a/epan/dissectors/packet-http.c +++ b/epan/dissectors/packet-http.c @@ -43,6 +43,7 @@ #include <epan/to_str.h> #include <epan/req_resp_hdrs.h> #include <epan/proto_data.h> +#include <epan/export_object.h> #include <wsutil/base64.h> #include "packet-http.h" @@ -350,6 +351,34 @@ static dissector_table_t port_subdissector_table; static dissector_table_t media_type_subdissector_table; static heur_dissector_list_t heur_subdissector_list; + +static gboolean +http_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data) +{ + export_object_list_t *object_list = (export_object_list_t *)tapdata; + const http_eo_t *eo_info = (const http_eo_t *)data; + export_object_entry_t *entry; + + if(eo_info) { /* We have data waiting for us */ + /* These values will be freed when the Export Object window + * is closed. */ + entry = g_new(export_object_entry_t, 1); + + entry->pkt_num = pinfo->num; + entry->hostname = g_strdup(eo_info->hostname); + entry->content_type = g_strdup(eo_info->content_type); + entry->filename = g_path_get_basename(eo_info->filename); + entry->payload_len = eo_info->payload_len; + entry->payload_data = (guint8 *)g_memdup(eo_info->payload_data, eo_info->payload_len); + + object_list->add_entry(object_list->gui_data, entry); + + return TRUE; /* State changed - window should be redrawn */ + } else { + return FALSE; /* State unchanged - no window updates needed */ + } +} + /* --- HTTP Status Codes */ /* Note: The reference for uncommented entries is RFC 2616 */ const value_string vals_http_status_code[] = { @@ -3688,11 +3717,11 @@ proto_register_http(void) * Register for tapping */ http_tap = register_tap("http"); /* HTTP statistics tap */ - http_eo_tap = register_tap("http_eo"); /* HTTP Export Object tap */ http_follow_tap = register_tap("http_follow"); /* HTTP Follow tap */ register_follow_stream(proto_http, "http_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter, tcp_port_to_display, follow_tvb_tap_listener); + http_eo_tap = register_export_object(proto_http, http_eo_packet, NULL); } /* diff --git a/epan/dissectors/packet-imf.c b/epan/dissectors/packet-imf.c index bb6aeb1ff4..0998eac672 100644 --- a/epan/dissectors/packet-imf.c +++ b/epan/dissectors/packet-imf.c @@ -31,6 +31,7 @@ #include <wsutil/str_util.h> #include <epan/tap.h> +#include <epan/export_object.h> #include "packet-ber.h" #include "packet-http.h" @@ -154,6 +155,35 @@ static dissector_handle_t imf_handle; static expert_field ei_imf_unknown_param = EI_INIT; + +static gboolean +imf_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data) +{ + export_object_list_t *object_list = (export_object_list_t *)tapdata; + const imf_eo_t *eo_info = (const imf_eo_t *)data; + export_object_entry_t *entry; + + if(eo_info) { /* We have data waiting for us */ + /* These values will be freed when the Export Object window + * is closed. */ + entry = g_new(export_object_entry_t, 1); + + entry->pkt_num = pinfo->num; + entry->hostname = NULL; + entry->content_type = g_strdup("EML file"); + entry->filename = g_strdup_printf("from_%s_subject_%s.eml", eo_info->sender_data, eo_info->subject_data); + entry->payload_len = eo_info->payload_len; + entry->payload_data = (guint8 *)g_memdup(eo_info->payload_data, eo_info->payload_len); + + object_list->add_entry(object_list->gui_data, entry); + + return TRUE; /* State changed - window should be redrawn */ + } else { + return FALSE; /* State unchanged - no window updates needed */ + } +} + + struct imf_field { char *name; /* field name - in lower case for matching purposes */ int *hf_id; /* wireshark field */ @@ -1302,7 +1332,7 @@ proto_register_imf(void) g_hash_table_insert(imf_field_table, (gpointer)f->name, (gpointer)f); /* Register for tapping */ - imf_eo_tap = register_tap("imf_eo"); /* IMF Export Object tap */ + imf_eo_tap = register_export_object(proto_imf, imf_eo_packet, NULL); } diff --git a/epan/dissectors/packet-smb.c b/epan/dissectors/packet-smb.c index fe9a8a614b..1e16747571 100644 --- a/epan/dissectors/packet-smb.c +++ b/epan/dissectors/packet-smb.c @@ -35,6 +35,7 @@ #include <epan/srt_table.h> #include <epan/expert.h> #include <epan/to_str.h> +#include <epan/export_object.h> #include "packet-windows-common.h" #include "packet-smb.h" @@ -968,6 +969,423 @@ smbstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const voi } /* + * Export object functionality + */ +/* These flags show what kind of data the object contains + (designed to be or'ed) */ +#define SMB_EO_CONTAINS_NOTHING 0x00 +#define SMB_EO_CONTAINS_READS 0x01 +#define SMB_EO_CONTAINS_WRITES 0x02 +#define SMB_EO_CONTAINS_READSANDWRITES 0x03 +#define LEGAL_FILENAME_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.- /\\{}[]=()&%$!,;.+&%$~#@" + +static const value_string smb_eo_contains_string[] = { + {SMB_EO_CONTAINS_NOTHING, "" }, + {SMB_EO_CONTAINS_READS, "R" }, + {SMB_EO_CONTAINS_WRITES, "W" }, + {SMB_EO_CONTAINS_READSANDWRITES, "R&W"}, + {0, NULL} +}; + +/* Strings that describes the SMB object type */ +static const value_string smb_fid_types[] = { + {SMB_FID_TYPE_UNKNOWN,"UNKNOWN"}, + {SMB_FID_TYPE_FILE,"FILE"}, + {SMB_FID_TYPE_DIR,"DIRECTORY (Not Implemented)"}, + {SMB_FID_TYPE_PIPE,"PIPE (Not Implemented)"}, + {0, NULL} +}; + +static const value_string smb2_fid_types[] = { + {SMB2_FID_TYPE_UNKNOWN,"UNKNOWN"}, + {SMB2_FID_TYPE_FILE,"FILE"}, + {SMB2_FID_TYPE_DIR,"DIRECTORY (Not Implemented)"}, + {SMB2_FID_TYPE_PIPE,"PIPE (Not Implemented)"}, + {SMB2_FID_TYPE_OTHER,"OTHER (Not Implemented)"}, + {0, NULL} +}; + + +/* This struct contains the relationship between + the row# in the export_object window and the file being captured; + the row# in this GSList will match the row# in the entry list */ + +typedef struct _active_file { + guint16 tid, uid, fid; + guint64 file_length; /* The last free reported offset. We treat it as the file length */ + guint64 data_gathered; /* The actual total of data gathered */ + guint8 flag_contains; /* What kind of data it contains */ + GSList *free_chunk_list; /* A list of virtual "holes" in the file stream stored in memory */ + gboolean is_out_of_memory; /* TRUE if we cannot allocate memory for this file */ +} active_file ; + +/* This is the GSList that will contain all the files that we are tracking */ +static GSList *GSL_active_files = NULL; + +/* We define a free chunk in a file as an start offset and end offset + Consider a free chunk as a "hole" in a file that we are capturing */ +typedef struct _free_chunk { + guint64 start_offset; + guint64 end_offset; +} free_chunk; + +/* insert_chunk function will recalculate the free_chunk_list, the data_size, + the end_of_file, and the data_gathered as appropriate. + It will also insert the data chunk that is coming in the right + place of the file in memory. + HINTS: + file->data_gathered contains the real data gathered independently from the file length + file->file_length contains the length of the file in memory, i.e., + the last offset captured. In most cases, the real + file length would be different. +*/ +static void +insert_chunk(active_file *file, export_object_entry_t *entry, const smb_eo_t *eo_info) +{ + gint nfreechunks = g_slist_length(file->free_chunk_list); + gint i; + free_chunk *current_free_chunk; + free_chunk *new_free_chunk; + guint64 chunk_offset = eo_info->smb_file_offset; + guint64 chunk_length = eo_info->payload_len; + guint64 chunk_end_offset = chunk_offset + chunk_length-1; + /* Size of file in memory */ + guint64 calculated_size = chunk_offset + chunk_length; + gpointer dest_memory_addr; + + /* Let's recalculate the file length and data gathered */ + if ((file->data_gathered == 0) && (nfreechunks == 0)) { + /* If this is the first entry for this file, we first create an initial free chunk */ + new_free_chunk = g_new(free_chunk, 1); + new_free_chunk->start_offset = 0; + new_free_chunk->end_offset = MAX(file->file_length, chunk_end_offset+1) - 1; + file->free_chunk_list = NULL; + file->free_chunk_list = g_slist_append(file->free_chunk_list, new_free_chunk); + nfreechunks += 1; + } else { + if (chunk_end_offset > file->file_length-1) { + new_free_chunk = g_new(free_chunk, 1); + new_free_chunk->start_offset = file->file_length; + new_free_chunk->end_offset = chunk_end_offset; + file->free_chunk_list = g_slist_append(file->free_chunk_list, new_free_chunk); + nfreechunks += 1; + } + } + file->file_length = MAX(file->file_length, chunk_end_offset+1); + + /* Recalculate each free chunk according with the incoming data chunk */ + for (i=0; i<nfreechunks; i++) { + current_free_chunk = (free_chunk *)g_slist_nth_data(file->free_chunk_list, i); + /* 1. data chunk before the free chunk? */ + /* -> free chunk is not altered and no new data gathered */ + if (chunk_end_offset<current_free_chunk->start_offset) { + continue; + } + /* 2. data chunk overlaps the first part of free_chunk */ + /* -> free chunk shrinks from the beginning */ + if (chunk_offset<=current_free_chunk->start_offset && chunk_end_offset>=current_free_chunk->start_offset && chunk_end_offset<current_free_chunk->end_offset) { + file->data_gathered += chunk_end_offset-current_free_chunk->start_offset+1; + current_free_chunk->start_offset=chunk_end_offset+1; + continue; + } + /* 3. data chunk overlaps completely the free chunk */ + /* -> free chunk is removed */ + if (chunk_offset<=current_free_chunk->start_offset && chunk_end_offset>=current_free_chunk->end_offset) { + file->data_gathered += current_free_chunk->end_offset-current_free_chunk->start_offset+1; + file->free_chunk_list = g_slist_remove(file->free_chunk_list, current_free_chunk); + nfreechunks -= 1; + if (nfreechunks == 0) { /* The free chunk list is empty */ + g_slist_free(file->free_chunk_list); + file->free_chunk_list = NULL; + break; + } + i--; + continue; + } + /* 4. data chunk is inside the free chunk */ + /* -> free chunk is split into two */ + if (chunk_offset>current_free_chunk->start_offset && chunk_end_offset<current_free_chunk->end_offset) { + new_free_chunk = g_new(free_chunk, 1); + new_free_chunk->start_offset = chunk_end_offset + 1; + new_free_chunk->end_offset = current_free_chunk->end_offset; + current_free_chunk->end_offset = chunk_offset-1; + file->free_chunk_list = g_slist_insert(file->free_chunk_list, new_free_chunk, i + 1); + file->data_gathered += chunk_length; + continue; + } + /* 5.- data chunk overlaps the end part of free chunk */ + /* -> free chunk shrinks from the end */ + if (chunk_offset>current_free_chunk->start_offset && chunk_offset<=current_free_chunk->end_offset && chunk_end_offset>=current_free_chunk->end_offset) { + file->data_gathered += current_free_chunk->end_offset-chunk_offset+1; + current_free_chunk->end_offset = chunk_offset-1; + continue; + } + /* 6.- data chunk is after the free chunk */ + /* -> free chunk is not altered and no new data gathered */ + if (chunk_offset>current_free_chunk->end_offset) { + continue; + } + } + + /* Now, let's insert the data chunk into memory + ...first, we shall be able to allocate the memory */ + if (!entry->payload_data) { + /* This is a New file */ + if (calculated_size > G_MAXSIZE) { + /* + * The argument to g_try_malloc() is + * a gsize, the maximum value of which is + * G_MAXSIZE. If the calculated size is + * bigger than that, we just say the attempt + * to allocate memory failed. + */ + entry->payload_data = NULL; + } else { + entry->payload_data = (guint8 *)g_try_malloc((gsize)calculated_size); + entry->payload_len = calculated_size; + } + if (!entry->payload_data) { + /* Memory error */ + file->is_out_of_memory = TRUE; + } + } else { + /* This is an existing file in memory */ + if (calculated_size > (guint64) entry->payload_len && + !file->is_out_of_memory) { + /* We need more memory */ + if (calculated_size > G_MAXSIZE) { + /* + * As for g_try_malloc(), so for + * g_try_realloc(). + */ + dest_memory_addr = NULL; + } else { + dest_memory_addr = g_try_realloc( + entry->payload_data, + (gsize)calculated_size); + } + if (!dest_memory_addr) { + /* Memory error */ + file->is_out_of_memory = TRUE; + /* We don't have memory for this file. + Free the current file content from memory */ + g_free(entry->payload_data); + entry->payload_data = NULL; + entry->payload_len = 0; + } else { + entry->payload_data = (guint8 *)dest_memory_addr; + entry->payload_len = calculated_size; + } + } + } + /* ...then, put the chunk of the file in the right place */ + if (!file->is_out_of_memory) { + dest_memory_addr = entry->payload_data + chunk_offset; + memmove(dest_memory_addr, eo_info->payload_data, eo_info->payload_len); + } +} + +/* We use this function to obtain the index in the GSL of a given file */ +static int +find_incoming_file(GSList *GSL_active_files_p, active_file *incoming_file) +{ + int i, row, last; + active_file *in_list_file; + + row = -1; + last = g_slist_length(GSL_active_files_p) - 1; + + /* We lookup in reverse order because it is more likely that the file + is one of the latest */ + for (i=last; i>=0; i--) { + in_list_file = (active_file *)g_slist_nth_data(GSL_active_files_p, i); + /* The best-working criteria of two identical files is that the file + that is the same of the file that we are analyzing is the last one + in the list that has the same tid and the same fid */ + /* note that we have excluded in_list_file->uid == incoming_file->uid + from the comparison, because a file can be opened by different + SMB users and it is still the same file */ + if (in_list_file->tid == incoming_file->tid && + in_list_file->fid == incoming_file->fid) { + row = i; + break; + } + } + + return row; +} + +static gboolean +smb_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data) +{ + export_object_list_t *object_list = (export_object_list_t *)tapdata; + const smb_eo_t *eo_info = (const smb_eo_t *)data; + + export_object_entry_t *entry; + export_object_entry_t *current_entry; + active_file incoming_file; + gint active_row; + active_file *new_file; + active_file *current_file; + guint8 contains; + gboolean is_supported_filetype; + gfloat percent; + + gchar *aux_smb_fid_type_string; + + if (eo_info->smbversion==1) { + /* Is this an eo_smb supported file_type? (right now we only support FILE) */ + is_supported_filetype = (eo_info->fid_type == SMB_FID_TYPE_FILE); + aux_smb_fid_type_string=g_strdup(try_val_to_str(eo_info->fid_type, smb_fid_types)); + + /* What kind of data this packet contains? */ + switch(eo_info->cmd) { + case SMB_COM_READ_ANDX: + case SMB_COM_READ: + contains = SMB_EO_CONTAINS_READS; + break; + case SMB_COM_WRITE_ANDX: + case SMB_COM_WRITE: + contains = SMB_EO_CONTAINS_WRITES; + break; + default: + contains = SMB_EO_CONTAINS_NOTHING; + break; + } + } else { + /* Is this an eo_smb supported file_type? (right now we only support FILE) */ + is_supported_filetype = (eo_info->fid_type == SMB2_FID_TYPE_FILE ); + aux_smb_fid_type_string=g_strdup(try_val_to_str(eo_info->fid_type, smb2_fid_types)); + + /* What kind of data this packet contains? */ + switch(eo_info->cmd) { + case SMB2_COM_READ: + contains = SMB_EO_CONTAINS_READS; + break; + case SMB2_COM_WRITE: + contains = SMB_EO_CONTAINS_WRITES; + break; + default: + contains = SMB_EO_CONTAINS_NOTHING; + break; + } + } + + + /* Is this data from an already tracked file or not? */ + incoming_file.tid = eo_info->tid; + incoming_file.uid = eo_info->uid; + incoming_file.fid = eo_info->fid; + active_row = find_incoming_file(GSL_active_files, &incoming_file); + + if (active_row == -1) { /* This is a new-tracked file */ + /* Construct the entry in the list of active files */ + entry = g_new(export_object_entry_t, 1); + entry->payload_data = NULL; + entry->payload_len = 0; + new_file = (active_file *)g_malloc(sizeof(active_file)); + new_file->tid = incoming_file.tid; + new_file->uid = incoming_file.uid; + new_file->fid = incoming_file.fid; + new_file->file_length = eo_info->end_of_file; + new_file->flag_contains = contains; + new_file->free_chunk_list = NULL; + new_file->data_gathered = 0; + new_file->is_out_of_memory = FALSE; + entry->pkt_num = pinfo->num; + + entry->hostname=g_filename_display_name(g_strcanon(eo_info->hostname,LEGAL_FILENAME_CHARS,'?')); + entry->filename=g_filename_display_name(g_strcanon(eo_info->filename,LEGAL_FILENAME_CHARS,'?')); + + /* Insert the first chunk in the chunk list of this file */ + if (is_supported_filetype) { + insert_chunk(new_file, entry, eo_info); + } + + if (new_file->is_out_of_memory) { + entry->content_type = + g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]", + aux_smb_fid_type_string, + new_file->data_gathered, + new_file->file_length, + try_val_to_str(contains, smb_eo_contains_string)); + } else { + if (new_file->file_length > 0) { + percent = (gfloat) (100*new_file->data_gathered/new_file->file_length); + } else { + percent = 0.0f; + } + + entry->content_type = + g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]", + aux_smb_fid_type_string, + new_file->data_gathered, + new_file->file_length, + try_val_to_str(contains, smb_eo_contains_string), + percent); + } + + object_list->add_entry(object_list->gui_data, entry); + GSL_active_files = g_slist_append(GSL_active_files, new_file); + } + else if (is_supported_filetype) { + current_file = (active_file *)g_slist_nth_data(GSL_active_files, active_row); + /* Recalculate the current file flags */ + current_file->flag_contains = current_file->flag_contains|contains; + current_entry = object_list->get_entry(object_list->gui_data, active_row); + + insert_chunk(current_file, current_entry, eo_info); + + /* Modify the current_entry object_type string */ + if (current_file->is_out_of_memory) { + current_entry->content_type = + g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]", + aux_smb_fid_type_string, + current_file->data_gathered, + current_file->file_length, + try_val_to_str(current_file->flag_contains, smb_eo_contains_string)); + } else { + percent = (gfloat) (100*current_file->data_gathered/current_file->file_length); + current_entry->content_type = + g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]", + aux_smb_fid_type_string, + current_file->data_gathered, + current_file->file_length, + try_val_to_str(current_file->flag_contains, smb_eo_contains_string), + percent); + } + } + + return TRUE; /* State changed - window should be redrawn */ +} + +/* This is the eo_reset_cb function that is used in the export_object module + to cleanup any previous private data of the export object functionality before perform + the eo_reset function or when the window closes */ +void +smb_eo_cleanup(void) +{ + int i, last; + active_file *in_list_file; + + /* Free any previous data structures used in previous invocation to the + export_object_smb function */ + last = g_slist_length(GSL_active_files); + if (GSL_active_files) { + for (i=last-1; i>=0; i--) { + in_list_file = (active_file *)g_slist_nth_data(GSL_active_files, i); + if (in_list_file->free_chunk_list) { + g_slist_free(in_list_file->free_chunk_list); + in_list_file->free_chunk_list = NULL; + } + g_free(in_list_file); + } + g_slist_free(GSL_active_files); + GSL_active_files = NULL; + } +} + +/* * Macros for use in the main dissector routines for an SMB. */ @@ -20621,12 +21039,11 @@ proto_register_smb(void) register_init_routine(smb_trans_reassembly_init); smb_tap = register_tap("smb"); - /* Register the tap for the "Export Object" function */ - smb_eo_tap = register_tap("smb_eo"); /* SMB Export Object tap */ - register_dissector("smb", dissect_smb, proto_smb); register_srt_table(proto_smb, NULL, 3, smbstat_packet, smbstat_init, NULL); + /* Register the tap for the "Export Object" function */ + smb_eo_tap = register_export_object(proto_smb, smb_eo_packet, smb_eo_cleanup); } void diff --git a/epan/dissectors/packet-tftp.c b/epan/dissectors/packet-tftp.c index b60c716222..ca108b0f57 100644 --- a/epan/dissectors/packet-tftp.c +++ b/epan/dissectors/packet-tftp.c @@ -46,6 +46,7 @@ #include <epan/expert.h> #include <epan/prefs.h> #include <epan/tap.h> +#include <epan/export_object.h> #include "packet-tftp.h" @@ -140,6 +141,94 @@ static const value_string tftp_error_code_vals[] = { static int tftp_eo_tap = -1; +/* A list of block list entries to delete from cleanup callback when window is closed. */ +typedef struct eo_info_dynamic_t { + gchar *filename; + GSList *block_list; +} eo_info_dynamic_t; +static GSList *s_dynamic_info_list = NULL; + +/* Tap function */ +static gboolean +tftp_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data) +{ + export_object_list_t *object_list = (export_object_list_t *)tapdata; + const tftp_eo_t *eo_info = (const tftp_eo_t *)data; + export_object_entry_t *entry; + + GSList *block_iterator; + guint payload_data_offset = 0; + eo_info_dynamic_t *dynamic_info; + + /* These values will be freed when the Export Object window is closed. */ + entry = g_new(export_object_entry_t, 1); + + /* Remember which frame had the last block of the file */ + entry->pkt_num = pinfo->num; + + /* Copy filename */ + entry->filename = g_path_get_basename(eo_info->filename); + + /* Iterate over list of blocks and concatenate into contiguous memory */ + entry->payload_len = eo_info->payload_len; + entry->payload_data = (guint8 *)g_try_malloc((gsize)entry->payload_len); + for (block_iterator = eo_info->block_list; block_iterator; block_iterator = block_iterator->next) { + file_block_t *block = (file_block_t*)block_iterator->data; + memcpy(entry->payload_data + payload_data_offset, + block->data, + block->length); + payload_data_offset += block->length; + } + + /* These 2 fields not used */ + entry->hostname = NULL; + entry->content_type = NULL; + + /* Add to list of entries to be cleaned up. eo_info is only packet scope, so + need to make list only of block list now */ + dynamic_info = g_new(eo_info_dynamic_t, 1); + dynamic_info->filename = eo_info->filename; + dynamic_info->block_list = eo_info->block_list; + s_dynamic_info_list = g_slist_append(s_dynamic_info_list, (eo_info_dynamic_t*)dynamic_info); + + /* Pass out entry to the GUI */ + object_list->add_entry(object_list->gui_data, entry); + + return TRUE; /* State changed - window should be redrawn */ +} + +/* Clean up the stored parts of a single tapped entry */ +static void cleanup_tftp_eo(eo_info_dynamic_t *dynamic_info) +{ + GSList *block_iterator; + /* Free the filename */ + g_free(dynamic_info->filename); + + /* Walk list of block items */ + for (block_iterator = dynamic_info->block_list; block_iterator; block_iterator = block_iterator->next) { + file_block_t *block = (file_block_t*)(block_iterator->data); + /* Free block data */ + wmem_free(NULL, block->data); + + /* Free block itself */ + g_free(block); + } +} + +/* Callback for freeing up data supplied with taps. The taps themselves only have + packet scope, so only store/free dynamic memory pointers */ +void tftp_eo_cleanup(void) +{ + /* Cleanup each entry in the global list */ + GSList *dynamic_iterator; + for (dynamic_iterator = s_dynamic_info_list; dynamic_iterator; dynamic_iterator = dynamic_iterator->next) { + eo_info_dynamic_t *dynamic_info = (eo_info_dynamic_t*)dynamic_iterator->data; + cleanup_tftp_eo(dynamic_info); + } + /* List is empty again */ + s_dynamic_info_list = NULL; +} + static void tftp_dissect_options(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint16 opcode, tftp_conv_info_t *tftp_info) @@ -684,7 +773,7 @@ proto_register_tftp(void) prefs_register_protocol(proto_tftp, apply_tftp_prefs); /* Register the tap for the "Export Object" function */ - tftp_eo_tap = register_tap("tftp_eo"); /* TFTP Export Object tap */ + tftp_eo_tap = register_export_object(proto_tftp, tftp_eo_packet, tftp_eo_cleanup); } void diff --git a/epan/export_object.c b/epan/export_object.c new file mode 100644 index 0000000000..5c7d38fb6e --- /dev/null +++ b/epan/export_object.c @@ -0,0 +1,206 @@ +/* export_object.c + * GUI independent helper routines common to all export object taps. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <string.h> + +#include "proto.h" +#include "packet_info.h" +#include "export_object.h" + +struct register_eo { + int proto_id; /* protocol id (0-indexed) */ + const char* tap_listen_str; /* string used in register_tap_listener (NULL to use protocol name) */ + tap_packet_cb eo_func; /* function to be called for new incoming packets for SRT */ + export_object_gui_reset_cb reset_cb; /* function to parse parameters of optional arguments of tap string */ +}; + +static GSList *registered_eo_tables = NULL; + +static gint +insert_sorted_by_table_name(gconstpointer aparam, gconstpointer bparam) +{ + const register_eo_t *a = (const register_eo_t *)aparam; + const register_eo_t *b = (const register_eo_t *)bparam; + + return g_ascii_strcasecmp(proto_get_protocol_filter_name(a->proto_id), proto_get_protocol_filter_name(b->proto_id)); +} + +int +register_export_object(const int proto_id, tap_packet_cb export_packet_func, export_object_gui_reset_cb reset_cb) +{ + register_eo_t *table; + DISSECTOR_ASSERT(export_packet_func); + + table = g_new(register_eo_t,1); + + table->proto_id = proto_id; + table->tap_listen_str = g_strdup_printf("%s_eo", proto_get_protocol_filter_name(proto_id)); + table->eo_func = export_packet_func; + table->reset_cb = reset_cb; + + registered_eo_tables = g_slist_insert_sorted(registered_eo_tables, table, insert_sorted_by_table_name); + return register_tap(table->tap_listen_str); +} + +int get_eo_proto_id(register_eo_t* eo) +{ + if (!eo) { + return -1; + } + return eo->proto_id; +} + +const char* get_eo_tap_listener_name(register_eo_t* eo) +{ + return eo->tap_listen_str; +} + +tap_packet_cb get_eo_packet_func(register_eo_t* eo) +{ + return eo->eo_func; +} + +export_object_gui_reset_cb get_eo_reset_func(register_eo_t* eo) +{ + return eo->reset_cb; +} + +register_eo_t* get_eo_by_name(const char* name) +{ + guint i, size = g_slist_length(registered_eo_tables); + register_eo_t* eo; + GSList *slist; + + for (i = 0; i < size; i++) { + slist = g_slist_nth(registered_eo_tables, i); + eo = (register_eo_t*)slist->data; + + if (strcmp(name, proto_get_protocol_filter_name(eo->proto_id)) == 0) + return eo; + } + + return NULL; +} + +void eo_iterate_tables(GFunc func, gpointer user_data) +{ + g_slist_foreach(registered_eo_tables, func, user_data); +} + +static GString *eo_rename(GString *gstr, int dupn) +{ + GString *gstr_tmp; + gchar *tmp_ptr; + GString *ext_str; + + gstr_tmp = g_string_new("("); + g_string_append_printf (gstr_tmp, "%d)", dupn); + if ( (tmp_ptr = strrchr(gstr->str, '.')) != NULL ) { + /* Retain the extension */ + ext_str = g_string_new(tmp_ptr); + gstr = g_string_truncate(gstr, gstr->len - ext_str->len); + if ( gstr->len >= (EXPORT_OBJECT_MAXFILELEN - (strlen(gstr_tmp->str) + ext_str->len)) ) + gstr = g_string_truncate(gstr, EXPORT_OBJECT_MAXFILELEN - (strlen(gstr_tmp->str) + ext_str->len)); + gstr = g_string_append(gstr, gstr_tmp->str); + gstr = g_string_append(gstr, ext_str->str); + g_string_free(ext_str, TRUE); + } + else { + if ( gstr->len >= (EXPORT_OBJECT_MAXFILELEN - strlen(gstr_tmp->str)) ) + gstr = g_string_truncate(gstr, EXPORT_OBJECT_MAXFILELEN - strlen(gstr_tmp->str)); + gstr = g_string_append(gstr, gstr_tmp->str); + } + g_string_free(gstr_tmp, TRUE); + return gstr; +} + +GString * +eo_massage_str(const gchar *in_str, gsize maxlen, int dupn) +{ + gchar *tmp_ptr; + /* The characters in "reject" come from: + * http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx. + * Add to the list as necessary for other OS's. + */ + const gchar *reject = "<>:\"/\\|?*" + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"; + GString *out_str; + GString *ext_str; + + out_str = g_string_new(""); + + /* Find all disallowed characters/bytes and replace them with %xx */ + while ( (tmp_ptr = strpbrk(in_str, reject)) != NULL ) { + out_str = g_string_append_len(out_str, in_str, tmp_ptr - in_str); + g_string_append_printf(out_str, "%%%02x", *tmp_ptr); + in_str = tmp_ptr + 1; + } + out_str = g_string_append(out_str, in_str); + if ( out_str->len > maxlen ) { + if ( (tmp_ptr = strrchr(out_str->str, '.')) != NULL ) { + /* Retain the extension */ + ext_str = g_string_new(tmp_ptr); + out_str = g_string_truncate(out_str, maxlen - ext_str->len); + out_str = g_string_append(out_str, ext_str->str); + g_string_free(ext_str, TRUE); + } + else + out_str = g_string_truncate(out_str, maxlen); + } + if ( dupn != 0 ) + out_str = eo_rename(out_str, dupn); + return out_str; +} + +const char * +eo_ct2ext(const char *content_type) +{ + /* TODO: Map the content type string to an extension string. If no match, + * return NULL. */ + return content_type; +} + +void eo_free_entry(export_object_entry_t *entry) +{ + g_free(entry->hostname); + g_free(entry->content_type); + g_free(entry->filename); + g_free(entry->payload_data); + + g_free(entry); +} +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/export_object.h b/epan/export_object.h new file mode 100644 index 0000000000..bd068e87c4 --- /dev/null +++ b/epan/export_object.h @@ -0,0 +1,155 @@ +/* export_object.h + * GUI independent helper routines common to all export object taps. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __EXPORT_OBJECT_H__ +#define __EXPORT_OBJECT_H__ + +#include "tap.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct _export_object_entry_t { + guint32 pkt_num; + gchar *hostname; + gchar *content_type; + gchar *filename; + /* We need to store a 64 bit integer to hold a file length + (was guint payload_len;) */ + gint64 payload_len; + guint8 *payload_data; +} export_object_entry_t; + +#define EXPORT_OBJECT_MAXFILELEN 255 + +typedef void (*export_object_object_list_add_entry_cb)(void* gui_data, struct _export_object_entry_t *entry); +typedef export_object_entry_t* (*export_object_object_list_get_entry_cb)(void* gui_data, int row); + +typedef struct _export_object_list_t { + export_object_object_list_add_entry_cb add_entry; //GUI specific handler for adding an object entry + export_object_object_list_get_entry_cb get_entry; //GUI specific handler for retrieving an object entry + void* gui_data; //GUI specific data (for UI representation) +} export_object_list_t; + +/** Structure for information about a registered exported object */ +typedef struct register_eo register_eo_t; + +/* When a protocol needs intermediate data structures to construct the +export objects, then it must specify a function that cleans up all +those data structures. This function is passed to export_object_window +and called when tap reset or windows closes occurs. If no function is needed +a NULL value should be passed instead */ +typedef void (*export_object_gui_reset_cb)(void); + +/** Register the export object handler for the Export Object windows. + * + * @param proto_id is the protocol with objects to export + * @param export_packet_func the tap processing function + * @param reset_cb handles clearing intermediate data structures constructed + * for exporting objects. If no function is needed a NULL value should be passed instead + * @return Tap id registered for the Export Object + */ +WS_DLL_PUBLIC int register_export_object(const int proto_id, tap_packet_cb export_packet_func, export_object_gui_reset_cb reset_cb); + +/** Get protocol ID from Export Object + * + * @param eo Registered Export Object + * @return protocol id of Export Object + */ +WS_DLL_PUBLIC int get_eo_proto_id(register_eo_t* eo); + +/** Get string for register_tap_listener call. Typically of the form <dissector_name>_eo + * + * @param eo Registered Export Object + * @return string for register_tap_listener call + */ +WS_DLL_PUBLIC const char* get_eo_tap_listener_name(register_eo_t* eo); + +/** Get tap function handler from Export Object + * + * @param eo Registered Export Object + * @return tap function handler of Export Object + */ +WS_DLL_PUBLIC tap_packet_cb get_eo_packet_func(register_eo_t* eo); + +/** Get tap reset function handler from Export Object + * + * @param eo Registered Export Object + * @return tap function handler of Export Object + */ +WS_DLL_PUBLIC export_object_gui_reset_cb get_eo_reset_func(register_eo_t* eo); + +/** Get Export Object by its short protocol name + * + * @param name short protocol name to fetch. + * @return Export Object handler pointer or NULL. + */ +WS_DLL_PUBLIC register_eo_t* get_eo_by_name(const char* name); + +/** Iterator to walk Export Object list and execute func + * + * @param func action to be performed on all Export Objects + * @param user_data any data needed to help perform function + */ +WS_DLL_PUBLIC void eo_iterate_tables(GFunc func, gpointer user_data); + +/** Find all disallowed characters/bytes and replace them with %xx + * + * @param in_str string to massage + * @param maxlen maximum size a string can be post massage + * @param dup return a copy of the massaged string (?) + * @return massaged string + */ +WS_DLL_PUBLIC GString *eo_massage_str(const gchar *in_str, gsize maxlen, int dup); + +/** Map the content type string to an extension string + * + * @param content_type content type to match with extension string + * @return extension string for content type + */ +WS_DLL_PUBLIC const char *eo_ct2ext(const char *content_type); + +/** Free the contents of export_object_entry_t structure + * + * @param entry export_object_entry_t structure to be freed + */ +WS_DLL_PUBLIC void eo_free_entry(export_object_entry_t *entry); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __EXPORT_OBJECT_H__ */ + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |