diff --git a/CMakeLists.txt b/CMakeLists.txt index b0a2d8fd86..942bb3b180 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1385,6 +1385,7 @@ set(TSHARK_TAP_SRC ui/cli/tap-comparestat.c ui/cli/tap-diameter-avp.c ui/cli/tap-expert.c + ui/cli/tap-exportobject.c ui/cli/tap-endpoints.c ui/cli/tap-follow.c ui/cli/tap-funnel.c diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols index d4cb226876..02c88dab30 100644 --- a/debian/libwireshark0.symbols +++ b/debian/libwireshark0.symbols @@ -486,6 +486,10 @@ libwireshark.so.0 libwireshark0 #MINVER# elem_tv_short@Base 1.9.1 elem_v@Base 1.9.1 elem_v_short@Base 1.9.1 + eo_ct2ext@Base 2.3.0 + eo_free_entry@Base 2.3.0 + eo_iterate_tables@Base 2.3.0 + eo_massage_str@Base 2.3.0 epan_cleanup@Base 1.9.1 epan_dissect_cleanup@Base 1.9.1 epan_dissect_fake_protocols@Base 1.9.1 @@ -719,6 +723,11 @@ libwireshark.so.0 libwireshark0 #MINVER# get_dissector_table_selector_type@Base 1.9.1 get_dissector_table_ui_name@Base 1.9.1 get_ebcdic_string@Base 1.12.0~rc1 + get_eo_by_name@Base 2.3.0 + get_eo_packet_func@Base 2.3.0 + get_eo_proto_id@Base 2.3.0 + get_eo_reset_func@Base 2.3.0 + get_eo_tap_listener_name@Base 2.3.0 get_eth_hashtable@Base 1.12.0~rc1 get_ether_name@Base 1.9.1 get_follow_address_func@Base 2.1.0 @@ -1210,6 +1219,7 @@ libwireshark.so.0 libwireshark0 #MINVER# register_depend_dissector@Base 2.1.0 register_dissector@Base 2.1.0 register_dissector_table@Base 1.9.1 + register_export_object@Base 2.3.0 register_export_pdu_tap@Base 1.99.0 register_follow_stream@Base 2.1.0 register_final_registration_routine@Base 1.9.1 diff --git a/doc/tshark.pod b/doc/tshark.pod index ac206fed1e..baa78e8cea 100644 --- a/doc/tshark.pod +++ b/doc/tshark.pod @@ -53,6 +53,7 @@ S<[ B<-y> Ecapture link typeE ]> S<[ B<-Y> EdisplaY filterE ]> S<[ B<-z> EstatisticsE ]> S<[ B<--capture-comment> EcommentE ]> +S<[ B<--export-objects> EprotocolE,EdestdirE ]> S<[ Ecapture filterE ]> B @@ -1569,6 +1570,18 @@ Add a capture comment to the output file. This option is only available if a new output file in pcapng format is created. Only one capture comment may be set per output file. +=item --export-objects EprotocolE,EdestdirE + +Export all objects within a protocol into directory B. The available +values for B can be listed with B<--export-objects help>. + +The objects are directly saved in the given directory. Filenames are dependent +on the dissector, but typically it is named after the basename of a file. +Duplicate files are not overwritten, instead an increasing number is appended +before the file extension. + +This interface is subject to change, adding the possibility to filter on files. + =item --disable-protocol Eproto_nameE Disable dissection of proto_name. diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc index 1bf32ac781..2b9b7d6110 100644 --- a/docbook/release-notes.asciidoc +++ b/docbook/release-notes.asciidoc @@ -37,6 +37,7 @@ since version 2.2.0: * GTK+ is disabled by default in Autotools and CMake. * SS7 Point Codes can now be resolved into names with a hosts-like file. * Wireshark can now go fullscreen to have more room for packets. +* TShark can now export objects like the other GUI interfaces. //=== Removed Dissectors 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 #include #include +#include #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 #include #include +#include #include #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 #include +#include #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 #include #include +#include #include "packet-windows-common.h" #include "packet-smb.h" @@ -967,6 +968,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; ifree_chunk_list, i); + /* 1. data chunk before the free chunk? */ + /* -> free chunk is not altered and no new data gathered */ + if (chunk_end_offsetstart_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_offsetend_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_offsetend_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 #include #include +#include #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 + * 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 + +#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 + * 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 _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: + */ diff --git a/tshark.c b/tshark.c index ff290c2f15..79bc27a81b 100644 --- a/tshark.c +++ b/tshark.c @@ -85,6 +85,7 @@ #include "ui/ui_util.h" #include "ui/decode_as_utils.h" #include "ui/cli/tshark-tap.h" +#include "ui/cli/tap-exportobject.h" #include "ui/tap_export_pdu.h" #include "ui/dissect_opts.h" #include "register.h" @@ -396,6 +397,8 @@ print_usage(FILE *output) fprintf(output, " --capture-comment \n"); fprintf(output, " add a capture comment to the newly created\n"); fprintf(output, " output file (only for pcapng)\n"); + fprintf(output, " --export-objects , save exported objects for a protocol to\n"); + fprintf(output, " a directory named \"destdir\"\n"); fprintf(output, "\n"); fprintf(output, "Miscellaneous:\n"); @@ -538,6 +541,7 @@ main(int argc, char *argv[]) {"version", no_argument, NULL, 'v'}, LONGOPT_CAPTURE_COMMON LONGOPT_DISSECT_COMMON + {"export-objects", required_argument, NULL, LONGOPT_EXPORT_OBJECTS}, {0, 0, 0, 0 } }; gboolean arg_error = FALSE; @@ -1282,6 +1286,15 @@ main(int argc, char *argv[]) if (!dissect_opts_handle_opt(opt, optarg)) return 1; break; + case LONGOPT_EXPORT_OBJECTS: /* --export-objects */ + if (strcmp("help", optarg) == 0) { + fprintf(stderr, "tshark: The available export object types for the \"--export-objects\" option are:\n"); + eo_list_object_types(); + return 0; + } + if (!eo_tap_opt_add(optarg)) + return 1; + break; default: case '?': /* Bad flag - print usage message */ switch(optopt) { @@ -1572,6 +1585,9 @@ main(int argc, char *argv[]) of the filter. We can now process all the "-z" arguments. */ start_requested_stats(); + /* We can also enable specified taps for export object */ + start_exportobjects(); + /* At this point MATE will have registered its field array so we can check if the fields specified by the user are all good. */ diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 9b38bf9c93..5bd5ec051c 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -28,12 +28,7 @@ set(COMMON_UI_SRC console.c decode_as_utils.c dissect_opts.c - export_object.c - export_object_dicom.c - export_object_http.c - export_object_imf.c - export_object_smb.c - export_object_tftp.c + export_object_ui.c export_pdu_ui_utils.c help_url.c firewall_rules.c diff --git a/ui/Makefile.am b/ui/Makefile.am index 8359bf7358..3507f9acbd 100644 --- a/ui/Makefile.am +++ b/ui/Makefile.am @@ -55,12 +55,7 @@ WIRESHARK_UI_SRC = \ console.c \ decode_as_utils.c \ dissect_opts.c \ - export_object.c \ - export_object_dicom.c \ - export_object_http.c \ - export_object_imf.c \ - export_object_smb.c \ - export_object_tftp.c \ + export_object_ui.c \ export_pdu_ui_utils.c \ firewall_rules.c \ iface_lists.c \ @@ -102,7 +97,7 @@ WIRESHARK_UI_INCLUDES = \ console.h \ decode_as_utils.h \ dissect_opts.h \ - export_object.h \ + export_object_ui.h \ export_pdu_ui_utils.h \ last_open_dir.h \ file_dialog.h \ diff --git a/ui/cli/Makefile.am b/ui/cli/Makefile.am index e5414985e4..a9f1c3aa32 100644 --- a/ui/cli/Makefile.am +++ b/ui/cli/Makefile.am @@ -47,7 +47,9 @@ TSHARK_TAP_SRC = \ tap-comparestat.c \ tap-diameter-avp.c \ tap-endpoints.c \ + tap-endpoints.c \ tap-expert.c \ + tap-exportobject.c \ tap-follow.c \ tap-funnel.c \ tap-gsm_astat.c \ diff --git a/ui/cli/tap-exportobject.c b/ui/cli/tap-exportobject.c new file mode 100644 index 0000000000..7ea0f207b4 --- /dev/null +++ b/ui/cli/tap-exportobject.c @@ -0,0 +1,279 @@ +/* tap-exportobject.c + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include "tap-exportobject.h" + +/* XXX - This is effectively a copy of eo_save_entry with the "GUI alerts" + * removed to accomodate tshark + */ +static gboolean +local_eo_save_entry(const gchar *save_as_filename, export_object_entry_t *entry) +{ + int to_fd; + gint64 bytes_left; + int bytes_to_write; + ssize_t bytes_written; + guint8 *ptr; + + to_fd = ws_open(save_as_filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0644); + if(to_fd == -1) { /* An error occurred */ + return FALSE; + } + + /* + * The third argument to _write() on Windows is an unsigned int, + * so, on Windows, that's the size of the third argument to + * ws_write(). + * + * The third argument to write() on UN*X is a size_t, although + * the return value is an ssize_t, so one probably shouldn't + * write more than the max value of an ssize_t. + * + * In either case, there's no guarantee that a gint64 such as + * payload_len can be passed to ws_write(), so we write in + * chunks of, at most 2^31 bytes. + */ + ptr = entry->payload_data; + bytes_left = entry->payload_len; + while (bytes_left != 0) { + if (bytes_left > 0x40000000) + bytes_to_write = 0x40000000; + else + bytes_to_write = (int)bytes_left; + bytes_written = ws_write(to_fd, ptr, bytes_to_write); + if(bytes_written <= 0) { + ws_close(to_fd); + return FALSE; + } + bytes_left -= bytes_written; + ptr += bytes_written; + } + if (ws_close(to_fd) < 0) { + return FALSE; + } + + return TRUE; +} + +typedef struct _export_object_list_gui_t { + GSList *entries; + register_eo_t* eo; +} export_object_list_gui_t; + +static GHashTable* eo_opts = NULL; + +static void +list_exportobject_protocol(gpointer data, gpointer user_data _U_) +{ + register_eo_t *eo = (register_eo_t*)data; + + fprintf(stderr, " %s\n", proto_get_protocol_filter_name(get_eo_proto_id(eo))); +} + +void eo_list_object_types(void) +{ + eo_iterate_tables(list_exportobject_protocol, NULL); +} + +gboolean eo_tap_opt_add(const char *option_string) +{ + gchar** splitted; + + if (!eo_opts) + eo_opts = g_hash_table_new(g_str_hash,g_str_equal); + + splitted = g_strsplit(option_string, ",", 2); + + if ((splitted[0] == NULL) || (splitted[1] == NULL) || (get_eo_by_name(splitted[0]) == NULL)) + { + fprintf(stderr, "tshark: \"--export-objects\" are specified as: ,\n"); + fprintf(stderr, "tshark: The available export object types for the \"--export-objects\" option are:\n"); + eo_list_object_types(); + } + else + { + gchar* dir = (gchar*)g_hash_table_lookup(eo_opts, splitted[0]); + + /* Since we're saving all objects from a protocol, + it can only be listed once */ + if (dir == NULL) { + g_hash_table_insert(eo_opts, splitted[0], splitted[1]); + + g_free(splitted); + return TRUE; + } + else + { + fprintf(stderr, "tshark: \"--export-objects\" already specified protocol '%s'\n", splitted[0]); + } + } + + g_strfreev(splitted); + return FALSE; +} + +static void +object_list_add_entry(void *gui_data, export_object_entry_t *entry) +{ + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; + + object_list->entries = g_slist_append(object_list->entries, entry); +} + +static export_object_entry_t* +object_list_get_entry(void *gui_data, int row) { + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; + + return (export_object_entry_t *)g_slist_nth_data(object_list->entries, row); +} + +/* This is just for writing Exported Objects to a file */ +static void +eo_draw(void *tapdata) +{ + export_object_list_t *tap_object = (export_object_list_t *)tapdata; + export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data; + GSList *slist = object_list->entries; + export_object_entry_t *entry; + gboolean all_saved = TRUE; + gchar* save_in_path = (gchar*)g_hash_table_lookup(eo_opts, proto_get_protocol_filter_name(get_eo_proto_id(object_list->eo))); + GString *safe_filename = NULL; + gchar *save_as_fullpath = NULL; + int count = 0; + + if (!g_file_test(save_in_path, G_FILE_TEST_IS_DIR)) { + /* If the destination directory (or its parents) do not exist, create them. */ + if (g_mkdir_with_parents(save_in_path, 0755) == -1) { + fprintf(stderr, "Failed to create export objects output directory \"%s\": %s\n", + save_in_path, g_strerror(errno)); + return; + } + } + + if ((strlen(save_in_path) < EXPORT_OBJECT_MAXFILELEN)) { + while (slist) { + entry = (export_object_entry_t *)slist->data; + do { + g_free(save_as_fullpath); + if (entry->filename) { + safe_filename = eo_massage_str(entry->filename, + EXPORT_OBJECT_MAXFILELEN - strlen(save_in_path), count); + } else { + char generic_name[EXPORT_OBJECT_MAXFILELEN+1]; + const char *ext; + ext = eo_ct2ext(entry->content_type); + g_snprintf(generic_name, sizeof(generic_name), + "object%u%s%s", entry->pkt_num, ext ? "." : "", ext ? ext : ""); + safe_filename = eo_massage_str(generic_name, + EXPORT_OBJECT_MAXFILELEN - strlen(save_in_path), count); + } + save_as_fullpath = g_build_filename(save_in_path, safe_filename->str, NULL); + g_string_free(safe_filename, TRUE); + } while (g_file_test(save_as_fullpath, G_FILE_TEST_EXISTS) && ++count < 1000); + count = 0; + if (!local_eo_save_entry(save_as_fullpath, entry)) + all_saved = FALSE; + g_free(save_as_fullpath); + save_as_fullpath = NULL; + slist = slist->next; + } + } + else + { + all_saved = FALSE; + } + + if (!all_saved) + fprintf(stderr, "Export objects (%s): Some files could not be saved.\n", + proto_get_protocol_filter_name(get_eo_proto_id(object_list->eo))); +} + +static void +exportobject_handler(gpointer key, gpointer value _U_, gpointer user_data _U_) +{ + GString *error_msg; + export_object_list_t *tap_data; + export_object_list_gui_t *object_list; + register_eo_t* eo; + + eo = get_eo_by_name((const char*)key); + if (eo == NULL) + { + fprintf(stderr, "tshark: \"--export-objects\" INTERNAL ERROR '%s' protocol not found\n", (const char*)key); + return; + } + + tap_data = g_new0(export_object_list_t,1); + object_list = g_new0(export_object_list_gui_t,1); + + tap_data->add_entry = object_list_add_entry; + tap_data->get_entry = object_list_get_entry; + tap_data->gui_data = (void*)object_list; + + object_list->eo = eo; + + /* Data will be gathered via a tap callback */ + error_msg = register_tap_listener(get_eo_tap_listener_name(eo), tap_data, NULL, 0, + NULL, get_eo_packet_func(eo), eo_draw); + + if (error_msg) { + fprintf(stderr, "tshark: Can't register %s tap: %s\n", (const char*)key, error_msg->str); + g_string_free(error_msg, TRUE); + g_free(tap_data); + g_free(object_list); + return; + } +} + +void start_exportobjects(void) +{ + if (eo_opts != NULL) + g_hash_table_foreach(eo_opts, exportobject_handler, NULL); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/cli/tap-exportobject.h b/ui/cli/tap-exportobject.h new file mode 100644 index 0000000000..25cfa333e3 --- /dev/null +++ b/ui/cli/tap-exportobject.h @@ -0,0 +1,55 @@ +/* tap-exportobject.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 __TAP_EXPORT_OBJECT_H__ +#define __TAP_EXPORT_OBJECT_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define LONGOPT_EXPORT_OBJECTS 5001 + +void eo_list_object_types(void); + +/* will be called by main each time a --export-objects option is found */ +gboolean eo_tap_opt_add(const char *optarg); + +void start_exportobjects(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TAP_EXPORT_OBJECT_H__ */ + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/ui/export_object.h b/ui/export_object.h deleted file mode 100644 index af71eedbde..0000000000 --- a/ui/export_object.h +++ /dev/null @@ -1,89 +0,0 @@ -/* export_object.h - * Common routines for tracking & saving objects found in streams of data - * Copyright 2007, Stephen Fisher (see AUTHORS file) - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * 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__ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Common between protocols */ - -struct _export_object_list_t; -typedef struct _export_object_list_t export_object_list_t; - -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; - -void object_list_add_entry(export_object_list_t *object_list, export_object_entry_t *entry); -export_object_entry_t *object_list_get_entry(export_object_list_t *object_list, int row); - -gboolean eo_save_entry(const gchar *save_as_filename, export_object_entry_t *entry, gboolean show_err); -GString *eo_massage_str(const gchar *in_str, gsize maxlen, int dup); -const char *ct2ext(const char *content_type); - - -/* Protocol specific */ -gboolean eo_dicom_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, - const void *data); -gboolean eo_http_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, - const void *data); -gboolean eo_imf_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, - const void *data); -gboolean eo_smb_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, - const void *data); -gboolean eo_tftp_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, - const void *data); - -void eo_smb_cleanup(void); -void eo_tftp_cleanup(void); - - -#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: - */ diff --git a/ui/export_object_dicom.c b/ui/export_object_dicom.c deleted file mode 100644 index 5404abb54f..0000000000 --- a/ui/export_object_dicom.c +++ /dev/null @@ -1,78 +0,0 @@ -/* export_object_dicom.c - * Routines for tracking & saving objects found in DICOM streams - * See also: export_object.c / export_object.h for common code - * Copyright 2008, David Aggeler - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * 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 - -#include - -#include -#include - -#include "export_object.h" - -gboolean -eo_dicom_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 = (export_object_entry_t *)g_malloc(sizeof(export_object_entry_t)); - - entry->pkt_num = pinfo->num; - entry->hostname = eo_info->hostname; - entry->content_type = eo_info->content_type; - entry->filename = g_strdup(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, entry); - - return TRUE; /* State changed - window should be redrawn */ - } else { - return FALSE; /* State unchanged - no window updates needed */ - } -} -/* - * 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/ui/export_object_http.c b/ui/export_object_http.c deleted file mode 100644 index 3b314c4fb5..0000000000 --- a/ui/export_object_http.c +++ /dev/null @@ -1,75 +0,0 @@ -/* export_object_http.c - * Routines for tracking & saving objects found in HTTP streams - * See also: export_object.c / export_object.h for common code - * Copyright 2007, Stephen Fisher (see AUTHORS file) - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * 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 -#include - -#include "export_object.h" - - -gboolean -eo_http_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 = (export_object_entry_t *)g_malloc(sizeof(export_object_entry_t)); - - entry->pkt_num = pinfo->num; - entry->hostname = g_strdup(eo_info->hostname); - entry->content_type = g_strdup(eo_info->content_type); - entry->filename = g_strdup(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, entry); - - return TRUE; /* State changed - window should be redrawn */ - } else { - return FALSE; /* State unchanged - no window updates needed */ - } -} - -/* - * 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/ui/export_object_imf.c b/ui/export_object_imf.c deleted file mode 100644 index 5e4e275691..0000000000 --- a/ui/export_object_imf.c +++ /dev/null @@ -1,75 +0,0 @@ -/* export_object_imf.c - * Routines for tracking & saving objects found in IMF streams - * See also: export_object.c / export_object.h for common code - * Copyright 2016, Moshe Kaplan - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * 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 -#include - -#include "export_object.h" - - -gboolean -eo_imf_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 = (export_object_entry_t *)g_malloc(sizeof(export_object_entry_t)); - - 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, entry); - - return TRUE; /* State changed - window should be redrawn */ - } else { - return FALSE; /* State unchanged - no window updates needed */ - } -} - -/* - * 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/ui/export_object_smb.c b/ui/export_object_smb.c deleted file mode 100644 index 4ccd1c07f2..0000000000 --- a/ui/export_object_smb.c +++ /dev/null @@ -1,470 +0,0 @@ -/* export_object_smb.c - * Routines for tracking & saving objects (files) found in SMB streams - * See also: export_object.c / export_object.h for common code - * Initial file, prototypes and general structure initially copied - * from export_object_http.c - * - * Copyright 2010, David Perez & Jose Pico from TADDONG S.L. - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * 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 -#include -#include - -#include "export_object.h" - - -/* 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 */ - /* 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 = (free_chunk *)g_malloc(sizeof(free_chunk)); - 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 = (free_chunk *)g_malloc(sizeof(free_chunk)); - 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; ifree_chunk_list, i); - /* 1. data chunk before the free chunk? */ - /* -> free chunk is not altered and no new data gathered */ - if (chunk_end_offsetstart_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_offsetend_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_offsetend_offset) { - new_free_chunk = (free_chunk *)g_malloc(sizeof(free_chunk)); - 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; -} - -/* This is the function answering to the registered tap listener call */ -gboolean -eo_smb_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 = (export_object_entry_t *)g_malloc(sizeof(export_object_entry_t)); - 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, 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, 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_protocoldata_reset 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 -eo_smb_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; - } -} - -/* - * 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/ui/export_object_tftp.c b/ui/export_object_tftp.c deleted file mode 100644 index 11547aca38..0000000000 --- a/ui/export_object_tftp.c +++ /dev/null @@ -1,138 +0,0 @@ -/* export_object_tftp.c - * Routines for aving objects (files) found in TFTP sessions - * See also: export_object.c / export_object.h for common code - * Initial file, prototypes and general structure initially copied - * from export_object_smb.c - * - * Martin Mathieson - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * 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 -#include -#include - -#include "export_object.h" - -/* 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 */ -gboolean -eo_tftp_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 = (export_object_entry_t*)g_malloc(sizeof(export_object_entry_t)); - - /* Remember which frame had the last block of the file */ - entry->pkt_num = pinfo->num; - - /* Copy filename */ - entry->filename = g_strdup(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 = (eo_info_dynamic_t*)g_malloc(sizeof(eo_info_dynamic_t)); - 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, 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 eo_tftp_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; -} - -/* - * 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/ui/export_object.c b/ui/export_object_ui.c similarity index 52% rename from ui/export_object.c rename to ui/export_object_ui.c index 67e883f056..2db2fe5516 100644 --- a/ui/export_object.c +++ b/ui/export_object_ui.c @@ -1,4 +1,4 @@ -/* export_object.c +/* export_object_ui.c * Common routines for tracking & saving objects found in streams of data * Copyright 2007, Stephen Fisher (see AUTHORS file) * @@ -39,7 +39,7 @@ #include -#include "export_object.h" +#include "export_object_ui.h" gboolean eo_save_entry(const gchar *save_as_filename, export_object_entry_t *entry, gboolean show_err) @@ -102,89 +102,6 @@ eo_save_entry(const gchar *save_as_filename, export_object_entry_t *entry, gbool return TRUE; } - -#define HINIBBLE(x) (((x) >> 4) & 0xf) -#define LONIBBLE(x) ((x) & 0xf) -#define HEXTOASCII(x) (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'a')) -#define MAXFILELEN 255 - -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 >= (MAXFILELEN - (strlen(gstr_tmp->str) + ext_str->len)) ) - gstr = g_string_truncate(gstr, 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 >= (MAXFILELEN - strlen(gstr_tmp->str)) ) - gstr = g_string_truncate(gstr, 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); - out_str = g_string_append_c(out_str, '%'); - out_str = g_string_append_c(out_str, HEXTOASCII(HINIBBLE(*tmp_ptr))); - out_str = g_string_append_c(out_str, HEXTOASCII(LONIBBLE(*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 * -ct2ext(const char *content_type) -{ - /* TODO: Map the content type string to an extension string. If no match, - * return NULL. */ - return content_type; -} - /* * Editor modelines * diff --git a/ui/export_object_ui.h b/ui/export_object_ui.h new file mode 100644 index 0000000000..a1cbf81b58 --- /dev/null +++ b/ui/export_object_ui.h @@ -0,0 +1,55 @@ +/* export_object_ui.h + * Common routines for tracking & saving objects found in streams of data + * Copyright 2007, Stephen Fisher (see AUTHORS file) + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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_UI_H__ +#define __EXPORT_OBJECT_UI_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +/* Common between protocols */ + +gboolean eo_save_entry(const gchar *save_as_filename, export_object_entry_t *entry, gboolean show_err); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __EXPORT_OBJECT_UI_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: + */ diff --git a/ui/gtk/export_object_dlg.c b/ui/gtk/export_object_dlg.c index ee1c3af8b3..ba7ce9c136 100644 --- a/ui/gtk/export_object_dlg.c +++ b/ui/gtk/export_object_dlg.c @@ -32,7 +32,7 @@ #include -#include +#include #include #include "dlg_utils.h" @@ -43,13 +43,6 @@ #include "stock_icons.h" #include "export_object_dlg.h" -/* When a protocol needs intermediate data structures to construct the -export objects, then it must specifiy 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 (*eo_protocoldata_reset_cb)(void); - enum { EO_PKT_NUM_COLUMN, EO_HOSTNAME_COLUMN, @@ -59,22 +52,23 @@ enum { EO_NUM_COLUMNS /* must be last */ }; -struct _export_object_list_t { +typedef struct _export_object_list_gui_t { GSList *entries; GtkWidget *tree, *dlg; GtkTreeView *tree_view; GtkTreeIter *iter; GtkTreeStore *store; gint row_selected; -}; +} export_object_list_gui_t; -static eo_protocoldata_reset_cb eo_protocoldata_reset = NULL; +static export_object_gui_reset_cb eo_protocoldata_reset = NULL; static void eo_remember_this_row(GtkTreeModel *model _U_, GtkTreePath *path, GtkTreeIter *iter _U_, gpointer arg) { - export_object_list_t *object_list = (export_object_list_t *)arg; + export_object_list_t *tap_object = (export_object_list_t *)arg; + export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data; export_object_entry_t *entry; gint *path_index; @@ -102,23 +96,19 @@ eo_remember_row_num(GtkTreeSelection *sel, gpointer data) static void eo_win_destroy_cb(GtkWindow *win _U_, gpointer data) { - export_object_list_t *object_list = (export_object_list_t *)data; + export_object_list_t *tap_object = (export_object_list_t *)data; + export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data; export_object_entry_t *entry; GSList *slist = object_list->entries; - remove_tap_listener(object_list); + remove_tap_listener(tap_object); /* Free the GSList attributes */ while(slist) { entry = (export_object_entry_t *)slist->data; - - g_free(entry->hostname); - g_free(entry->content_type); - g_free(entry->filename); - wmem_free(wmem_file_scope(), entry->payload_data); + eo_free_entry(entry); slist = slist->next; - wmem_free(wmem_file_scope(), entry); } /* Free the GSList elements */ @@ -130,7 +120,7 @@ eo_win_destroy_cb(GtkWindow *win _U_, gpointer data) } static char * -gtk_eo_save_object_as_file(export_object_list_t *object_list, char *auxfilename) +gtk_eo_save_object_as_file(export_object_list_gui_t *object_list, char *auxfilename) { GtkWidget *save_as_w; char *pathname; @@ -156,7 +146,8 @@ gtk_eo_save_object_as_file(export_object_list_t *object_list, char *auxfilename) static void eo_save_clicked_cb(GtkWidget *widget _U_, gpointer arg) { - export_object_list_t *object_list = (export_object_list_t *)arg; + export_object_list_t *tap_object = (export_object_list_t *)arg; + export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data; export_object_entry_t *entry; GString *safe_filename = NULL; char *pathname; @@ -193,12 +184,12 @@ eo_save_clicked_cb(GtkWidget *widget _U_, gpointer arg) g_string_free(safe_filename, TRUE); } -#define MAXFILELEN 255 static void eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg) { gchar *save_as_fullpath = NULL; - export_object_list_t *object_list = (export_object_list_t *)arg; + export_object_list_t *tap_object = (export_object_list_t *)arg; + export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data; export_object_entry_t *entry; GtkWidget *save_in_w; GSList *slist = object_list->entries; @@ -216,21 +207,21 @@ eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg) entry = (export_object_entry_t *)slist->data; save_in_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_in_w)); - if ((strlen(save_in_path) < MAXFILELEN)) { + if ((strlen(save_in_path) < EXPORT_OBJECT_MAXFILELEN)) { do { g_free(save_as_fullpath); if (entry->filename) { safe_filename = eo_massage_str(entry->filename, - MAXFILELEN - strlen(save_in_path), count); + EXPORT_OBJECT_MAXFILELEN - strlen(save_in_path), count); } else { - char generic_name[MAXFILELEN+1]; + char generic_name[EXPORT_OBJECT_MAXFILELEN+1]; const char *ext; - ext = ct2ext(entry->content_type); + ext = eo_ct2ext(entry->content_type); g_snprintf(generic_name, sizeof(generic_name), "object%u%s%s", entry->pkt_num, ext ? "." : "", ext ? ext : ""); safe_filename = eo_massage_str(generic_name, - MAXFILELEN - strlen(save_in_path), count); + EXPORT_OBJECT_MAXFILELEN - strlen(save_in_path), count); } save_as_fullpath = g_build_filename( save_in_path, safe_filename->str, NULL); @@ -260,7 +251,8 @@ eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg) static void eo_reset(void *tapdata) { - export_object_list_t *object_list = (export_object_list_t *)tapdata; + export_object_list_t *tap_object = (export_object_list_t *)tapdata; + export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data; object_list->entries = NULL; object_list->iter = NULL; @@ -272,7 +264,8 @@ eo_reset(void *tapdata) static void eo_draw(void *tapdata) { - export_object_list_t *object_list = (export_object_list_t *)tapdata; + export_object_list_t *tap_object = (export_object_list_t *)tapdata; + export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data; export_object_entry_t *eo_entry; gchar *size_str; @@ -305,17 +298,23 @@ eo_draw(void *tapdata) } } -void object_list_add_entry(export_object_list_t *object_list, export_object_entry_t *entry) +static void +object_list_add_entry(void *gui_data, export_object_entry_t *entry) { + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; + object_list->entries = g_slist_append(object_list->entries, entry); } -export_object_entry_t *object_list_get_entry(export_object_list_t *object_list, int row) { +static export_object_entry_t* +object_list_get_entry(void *gui_data, int row) { + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; + return (export_object_entry_t *)g_slist_nth_data(object_list->entries, row); } static void -export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_packet, eo_protocoldata_reset_cb eo_protocoldata_resetfn) +export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_packet, export_object_gui_reset_cb eo_protocoldata_resetfn) { GtkWidget *sw; GtkCellRenderer *renderer; @@ -323,17 +322,23 @@ export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_ GtkTreeSelection *selection; GtkWidget *vbox, *bbox, *help_bt, *cancel_bt, *save_bt, *save_all_bt; GString *error_msg; - export_object_list_t *object_list; + export_object_list_t *tap_data; + export_object_list_gui_t *object_list; gchar *window_title; /* Initialize the pointer to the private data clearing function */ eo_protocoldata_reset = eo_protocoldata_resetfn; /* Initialize our object list structure */ - object_list = g_new0(export_object_list_t,1); + tap_data = g_new0(export_object_list_t,1); + object_list = g_new0(export_object_list_gui_t,1); + + tap_data->add_entry = object_list_add_entry; + tap_data->get_entry = object_list_get_entry; + tap_data->gui_data = (void*)object_list; /* Data will be gathered via a tap callback */ - error_msg = register_tap_listener(tapname, object_list, NULL, 0, + error_msg = register_tap_listener(tapname, tap_data, NULL, 0, eo_reset, tap_packet, eo_draw); @@ -342,6 +347,7 @@ export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Can't register %s tap: %s\n", name, error_msg->str); g_string_free(error_msg, TRUE); + g_free(tap_data); g_free(object_list); return; } @@ -425,7 +431,7 @@ export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_ gtk_container_add(GTK_CONTAINER(sw), object_list->tree); selection = gtk_tree_view_get_selection(object_list->tree_view); - g_signal_connect(selection, "changed", G_CALLBACK(eo_remember_row_num), object_list); + g_signal_connect(selection, "changed", G_CALLBACK(eo_remember_row_num), tap_data); bbox = dlg_button_row_new(GTK_STOCK_HELP, WIRESHARK_STOCK_SAVE_ALL, GTK_STOCK_SAVE_AS, GTK_STOCK_CANCEL, NULL); @@ -436,13 +442,12 @@ export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_ /* Save All button */ save_all_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_SAVE_ALL); - g_signal_connect(save_all_bt, "clicked", G_CALLBACK(eo_save_all_clicked_cb), - object_list); + g_signal_connect(save_all_bt, "clicked", G_CALLBACK(eo_save_all_clicked_cb), tap_data); gtk_widget_set_tooltip_text(save_all_bt, "Save all listed objects with their displayed filenames."); /* Save As button */ save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE_AS); - g_signal_connect(save_bt, "clicked", G_CALLBACK(eo_save_clicked_cb), object_list); + g_signal_connect(save_bt, "clicked", G_CALLBACK(eo_save_clicked_cb), tap_data); gtk_widget_set_tooltip_text(save_bt, "Saves the currently selected content to a file."); /* Cancel button */ @@ -455,8 +460,7 @@ export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_ /* Setup cancel/delete/destroy signal handlers */ g_signal_connect(object_list->dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL); - g_signal_connect(object_list->dlg, "destroy", - G_CALLBACK(eo_win_destroy_cb), object_list); + g_signal_connect(object_list->dlg, "destroy", G_CALLBACK(eo_win_destroy_cb), tap_data); window_set_cancel_button(object_list->dlg, cancel_bt, window_cancel_button_cb); @@ -467,30 +471,9 @@ export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_ cf_retap_packets(&cfile); } -void -eo_dicom_cb(GtkWidget *widget _U_, gpointer data _U_) +void exportobject_cb(register_eo_t *eo) { - export_object_window("dicom_eo", "DICOM", eo_dicom_packet, NULL); -} - -void -eo_http_cb(GtkWidget *widget _U_, gpointer data _U_) -{ - export_object_window("http_eo", "HTTP", eo_http_packet, NULL); -} - -void -eo_smb_cb(GtkWidget *widget _U_, gpointer data _U_) -{ - /* Call the export_object window */ - export_object_window("smb_eo", "SMB", eo_smb_packet, eo_smb_cleanup); -} - -void -eo_tftp_cb(GtkWidget *widget _U_, gpointer data _U_) -{ - /* Call the export_object window */ - export_object_window("tftp_eo", "TFTP", eo_tftp_packet, eo_tftp_cleanup); + export_object_window(get_eo_tap_listener_name(eo), proto_get_protocol_short_name(find_protocol_by_id(get_eo_proto_id(eo))), get_eo_packet_func(eo), get_eo_reset_func(eo)); } /* diff --git a/ui/gtk/export_object_dlg.h b/ui/gtk/export_object_dlg.h index 73fae69a34..72121df593 100644 --- a/ui/gtk/export_object_dlg.h +++ b/ui/gtk/export_object_dlg.h @@ -25,10 +25,8 @@ #ifndef __EXPORT_OBJECT_DLG_H__ #define __EXPORT_OBJECT_DLG_H__ -/* Protocol specific */ -void eo_dicom_cb(GtkWidget *widget _U_, gpointer data _U_); -void eo_http_cb(GtkWidget *widget _U_, gpointer data _U_); -void eo_smb_cb(GtkWidget *widget _U_, gpointer data _U_); -void eo_tftp_cb(GtkWidget *widget _U_, gpointer data _U_); +#include + +void exportobject_cb(register_eo_t *eo); #endif /* __EXPORT_OBJECT_DLG_H__ */ diff --git a/ui/gtk/main_menubar.c b/ui/gtk/main_menubar.c index 0b8aeacc60..907f65285d 100644 --- a/ui/gtk/main_menubar.c +++ b/ui/gtk/main_menubar.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "globals.h" #include @@ -821,11 +822,8 @@ static const char *ui_desc_menubar = " \n" " \n" " \n" -" \n" -" \n" -" \n" -" \n" -" \n" +" \n" +" \n" " \n" " \n" " \n" @@ -1280,10 +1278,6 @@ static const GtkActionEntry main_menu_bar_entries[] = { NULL, NULL, G_CALLBACK(export_pdml_cmd_cb) }, { "/File/ExportPacketDissections/JSON", NULL, "as \"_JSON\" file...", NULL, NULL, G_CALLBACK(export_json_cmd_cb) }, - { "/File/ExportObjects/HTTP", NULL, "_HTTP", NULL, NULL, G_CALLBACK(eo_http_cb) }, - { "/File/ExportObjects/DICOM", NULL, "_DICOM", NULL, NULL, G_CALLBACK(eo_dicom_cb) }, - { "/File/ExportObjects/SMB", NULL, "_SMB/SMB2", NULL, NULL, G_CALLBACK(eo_smb_cb) }, - { "/File/ExportObjects/TFTP", NULL, "_TFTP", NULL, NULL, G_CALLBACK(eo_tftp_cb) }, { "/Edit/Copy", NULL, "Copy", NULL, NULL, NULL }, @@ -2810,6 +2804,73 @@ menu_hostlist_list(capture_file *cf) conversation_table_iterate_tables(add_hostlist_menuitem, &conv_data); } +typedef struct { + guint merge_id; + GtkActionGroup *action_group; + int counter; +} eo_menu_t; + +static void +menu_exportobject_cb(GtkAction *action _U_, gpointer user_data) +{ + register_eo_t *eo = (register_eo_t*)user_data; + + exportobject_cb(eo); +} + +static void +add_export_object_menuitem(gpointer data, gpointer user_data) +{ + register_eo_t *eo = (register_eo_t*)data; + eo_menu_t *eo_menu_data = (eo_menu_t*)user_data; + gchar *action_name; + GtkAction *action; + + action_name = g_strdup_printf ("exportobject-%u", eo_menu_data->counter); + /*g_warning("action_name %s, filter_entry->name %s",action_name,filter_entry->name);*/ + action = (GtkAction *)g_object_new (GTK_TYPE_ACTION, + "name", action_name, + "label", proto_get_protocol_short_name(find_protocol_by_id(get_eo_proto_id(eo))), + "sensitive", TRUE, + NULL); + g_signal_connect (action, "activate", + G_CALLBACK (menu_exportobject_cb), eo); + gtk_action_group_add_action (eo_menu_data->action_group, action); + g_object_unref (action); + + gtk_ui_manager_add_ui (ui_manager_main_menubar, eo_menu_data->merge_id, + "/Menubar/FileMenu/ExportObjectsMenu/ExportObjects", + action_name, + action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + g_free(action_name); + eo_menu_data->counter++; +} + +static void +menu_export_object_list(void) +{ + GtkWidget *submenu_export_object; + eo_menu_t eo_data; + + eo_data.merge_id = gtk_ui_manager_new_merge_id (ui_manager_main_menubar); + + eo_data.action_group = gtk_action_group_new ("exportobject-list-group"); + + submenu_export_object = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/FileMenu/ExportObjects"); + if(!submenu_export_object){ + g_warning("menu_export_object_list: No submenu_exportobject_list found, path= /Menubar/FileMenu/ExportObjects"); + } + + gtk_ui_manager_insert_action_group (ui_manager_main_menubar, eo_data.action_group, 0); + g_object_set_data (G_OBJECT (ui_manager_main_menubar), + "exportobject-list-merge-id", GUINT_TO_POINTER (eo_data.merge_id)); + + eo_data.counter = 0; + eo_iterate_tables(add_export_object_menuitem, &eo_data); +} + static void menu_conversation_display_filter_cb(GtkAction *action _U_, gpointer data) { @@ -3253,6 +3314,7 @@ menus_init(void) menu_dissector_filter(&cfile); menu_conversation_list(&cfile); menu_hostlist_list(&cfile); + menu_export_object_list(); /* Add additional entries which may have been introduced by dissectors and/or plugins */ ws_menubar_external_menus(); @@ -4396,7 +4458,7 @@ set_menus_for_capture_file(capture_file *cf) set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportPacketDissections", FALSE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSelectedPacketBytes", FALSE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSSLSessionKeys", FALSE); - set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportObjects", FALSE); + set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportObjectsMenu", FALSE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportPDUs", FALSE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/Reload", FALSE); } else { @@ -4415,7 +4477,7 @@ set_menus_for_capture_file(capture_file *cf) set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportPacketDissections", TRUE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSelectedPacketBytes", TRUE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSSLSessionKeys", TRUE); - set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportObjects", TRUE); + set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportObjectsMenu", TRUE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportPDUs", TRUE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/Reload", TRUE); } @@ -4441,7 +4503,7 @@ set_menus_for_capture_in_progress(gboolean capture_in_progress) capture_in_progress); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportSSLSessionKeys", capture_in_progress); - set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportObjects", + set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/ExportObjectsMenu", capture_in_progress); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Set", !capture_in_progress); diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 3cd9d2e503..9899ad7477 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -60,6 +60,7 @@ set(WIRESHARK_QT_HEADERS endpoint_dialog.h expert_info_dialog.h export_dissection_dialog.h + export_object_action.h export_object_dialog.h export_pdu_dialog.h field_filter_edit.h @@ -227,6 +228,7 @@ set(WIRESHARK_QT_SRC enabled_protocols_dialog.cpp endpoint_dialog.cpp export_dissection_dialog.cpp + export_object_action.cpp export_object_dialog.cpp export_pdu_dialog.cpp field_filter_edit.cpp diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index f543f42c0a..bb83a45a6a 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -190,6 +190,7 @@ MOC_HDRS = \ endpoint_dialog.h \ expert_info_dialog.h \ export_dissection_dialog.h \ + export_object_action.h \ export_object_dialog.h \ export_pdu_dialog.h \ field_filter_edit.h \ @@ -471,6 +472,7 @@ WIRESHARK_QT_SRC = \ enabled_protocols_dialog.cpp \ endpoint_dialog.cpp \ export_dissection_dialog.cpp \ + export_object_action.cpp \ export_object_dialog.cpp \ export_pdu_dialog.cpp \ field_filter_edit.cpp \ diff --git a/ui/qt/export_object_action.cpp b/ui/qt/export_object_action.cpp new file mode 100644 index 0000000000..7b9363ba72 --- /dev/null +++ b/ui/qt/export_object_action.cpp @@ -0,0 +1,65 @@ +/* conversation_colorize_action.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#include +#include +#include +#include +#include +#include "export_object_action.h" + +#include + +#include "qt_ui_utils.h" + +ExportObjectAction::ExportObjectAction(QObject *parent, register_eo_t *eo) : + QAction(parent), + eo_(eo) +{ + if (eo_) { + setText( QString("%1%2").arg(proto_get_protocol_short_name(find_protocol_by_id(get_eo_proto_id(eo)))).arg(UTF8_HORIZONTAL_ELLIPSIS)); + } +} + +void ExportObjectAction::captureFileOpened() +{ + setEnabled(true); +} + +void ExportObjectAction::captureFileClosed() +{ + setEnabled(false); +} + +/* + * 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/ui/qt/export_object_action.h b/ui/qt/export_object_action.h new file mode 100644 index 0000000000..5328f24b31 --- /dev/null +++ b/ui/qt/export_object_action.h @@ -0,0 +1,64 @@ +/* export_object_action.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 EXPORTOBJECTACTION_H +#define EXPORTOBJECTACTION_H + +#include "config.h" + +#include +#include +#include + +#include + +// Actions for "Export Objects" menu items. + +class ExportObjectAction : public QAction +{ + Q_OBJECT +public: + ExportObjectAction(QObject *parent, register_eo_t *eo = NULL); + + register_eo_t* exportObject() {return eo_;} + +public slots: + void captureFileOpened(); + void captureFileClosed(); + +private: + register_eo_t *eo_; +}; + +#endif // EXPORTOBJECTACTION_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: + */ diff --git a/ui/qt/export_object_dialog.cpp b/ui/qt/export_object_dialog.cpp index 29126334ca..8592332feb 100644 --- a/ui/qt/export_object_dialog.cpp +++ b/ui/qt/export_object_dialog.cpp @@ -28,6 +28,7 @@ #include #include +#include "qt_ui_utils.h" #include "wireshark_application.h" #include @@ -37,14 +38,21 @@ extern "C" { -// object_list_add_entry and object_list_get_entry are defined in ui/export_object.h +static void +object_list_add_entry(void *gui_data, export_object_entry_t *entry) { + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; -void object_list_add_entry(export_object_list_t *object_list, export_object_entry_t *entry) { - if (object_list && object_list->eod) object_list->eod->addObjectEntry(entry); + if (object_list && object_list->eod) + object_list->eod->addObjectEntry(entry); } -export_object_entry_t *object_list_get_entry(export_object_list_t *object_list, int row) { - if (object_list && object_list->eod) return object_list->eod->objectEntry(row); +static export_object_entry_t* +object_list_get_entry(void *gui_data, int row) { + export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data; + + if (object_list && object_list->eod) + return object_list->eod->objectEntry(row); + return NULL; } @@ -54,21 +62,94 @@ export_object_entry_t *object_list_get_entry(export_object_list_t *object_list, static void eo_reset(void *tapdata) { - export_object_list_t *object_list = (export_object_list_t *) tapdata; + export_object_list_t *tap_object = (export_object_list_t *)tapdata; + export_object_list_gui_t *object_list = (export_object_list_gui_t *)tap_object->gui_data; if (object_list && object_list->eod) object_list->eod->resetObjects(); } } // extern "C" -ExportObjectDialog::ExportObjectDialog(QWidget &parent, CaptureFile &cf, ObjectType object_type) : + +enum { + COL_PACKET, + COL_HOSTNAME, + COL_CONTENT_TYPE, + COL_SIZE, + COL_FILENAME +}; + +enum { + export_object_row_type_ = 1000 +}; + +class ExportObjectTreeWidgetItem : public QTreeWidgetItem +{ +public: + ExportObjectTreeWidgetItem(QTreeWidget *parent, export_object_entry_t *entry) : + QTreeWidgetItem (parent, export_object_row_type_), + entry_(entry) + { + // Not perfect but better than nothing. + setTextAlignment(COL_SIZE, Qt::AlignRight); + } + ~ExportObjectTreeWidgetItem() { + eo_free_entry(entry_); + } + + export_object_entry_t *entry() { return entry_; } + + virtual QVariant data(int column, int role) const { + if (!entry_ || role != Qt::DisplayRole) { + return QTreeWidgetItem::data(column, role); + } + + switch (column) { + case COL_PACKET: + return QString::number(entry_->pkt_num); + case COL_HOSTNAME: + return entry_->hostname; + case COL_CONTENT_TYPE: + return entry_->content_type; + case COL_SIZE: + return file_size_to_qstring(entry_->payload_len); + case COL_FILENAME: + return entry_->filename; + default: + break; + } + return QTreeWidgetItem::data(column, role); + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (!entry_ || other.type() != export_object_row_type_) { + return QTreeWidgetItem::operator< (other); + } + + const ExportObjectTreeWidgetItem *other_row = static_cast(&other); + + switch (treeWidget()->sortColumn()) { + case COL_PACKET: + return entry_->pkt_num < other_row->entry_->pkt_num; + case COL_SIZE: + return entry_->payload_len < other_row->entry_->payload_len; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + +private: + export_object_entry_t *entry_; +}; + +ExportObjectDialog::ExportObjectDialog(QWidget &parent, CaptureFile &cf, register_eo_t* eo) : WiresharkDialog(parent, cf), eo_ui_(new Ui::ExportObjectDialog), save_bt_(NULL), save_all_bt_(NULL), - tap_name_(NULL), - name_(NULL), - tap_packet_(NULL), - eo_protocoldata_resetfn_(NULL) + eo_(eo) { QPushButton *close_bt; @@ -80,42 +161,17 @@ ExportObjectDialog::ExportObjectDialog(QWidget &parent, CaptureFile &cf, ObjectT eo_ui_->progressBar->setAttribute(Qt::WA_MacSmallSize, true); #endif - export_object_list_.eod = this; + eo_gui_data_.eod = this; - switch (object_type) { - case Dicom: - tap_name_ = "dicom_eo"; - name_ = "DICOM"; - tap_packet_ = eo_dicom_packet; - break; - case Http: - tap_name_ = "http_eo"; - name_ = "HTTP"; - tap_packet_ = eo_http_packet; - break; - case Imf: - tap_name_ = "imf_eo"; - name_ = "IMF"; - tap_packet_ = eo_imf_packet; - break; - case Smb: - tap_name_ = "smb_eo"; - name_ = "SMB"; - tap_packet_ = eo_smb_packet; - eo_protocoldata_resetfn_ = eo_smb_cleanup; - break; - case Tftp: - tap_name_ = "tftp_eo"; - name_ = "TFTP"; - tap_packet_ = eo_tftp_packet; - break; - } + export_object_list_.add_entry = object_list_add_entry; + export_object_list_.get_entry = object_list_get_entry; + export_object_list_.gui_data = (void*)&eo_gui_data_; save_bt_ = eo_ui_->buttonBox->button(QDialogButtonBox::Save); save_all_bt_ = eo_ui_->buttonBox->button(QDialogButtonBox::SaveAll); close_bt = eo_ui_->buttonBox->button(QDialogButtonBox::Close); - setWindowTitle(wsApp->windowTitleString(QStringList() << tr("Export") << tr("%1 object list").arg(name_))); + setWindowTitle(wsApp->windowTitleString(QStringList() << tr("Export") << tr("%1 object list").arg(proto_get_protocol_short_name(find_protocol_by_id(get_eo_proto_id(eo)))))); if (save_bt_) save_bt_->setEnabled(false); if (save_all_bt_) save_all_bt_->setEnabled(false); @@ -131,46 +187,37 @@ ExportObjectDialog::ExportObjectDialog(QWidget &parent, CaptureFile &cf, ObjectT ExportObjectDialog::~ExportObjectDialog() { delete eo_ui_; - export_object_list_.eod = NULL; + eo_gui_data_.eod = NULL; removeTapListeners(); } void ExportObjectDialog::addObjectEntry(export_object_entry_t *entry) { - QTreeWidgetItem *entry_item; - gchar *size_str; - if (!entry) return; - size_str = format_size(entry->payload_len, format_size_unit_bytes|format_size_prefix_si); - - entry_item = new QTreeWidgetItem(eo_ui_->objectTree); - entry_item->setData(0, Qt::UserRole, qVariantFromValue(entry)); - - entry_item->setText(0, QString().setNum(entry->pkt_num)); - entry_item->setText(1, entry->hostname); - entry_item->setText(2, entry->content_type); - entry_item->setText(3, size_str); - entry_item->setText(4, entry->filename); - g_free(size_str); - // Not perfect but better than nothing. - entry_item->setTextAlignment(3, Qt::AlignRight); + new ExportObjectTreeWidgetItem(eo_ui_->objectTree, entry); if (save_all_bt_) save_all_bt_->setEnabled(true); } export_object_entry_t *ExportObjectDialog::objectEntry(int row) { - QTreeWidgetItem *item = eo_ui_->objectTree->topLevelItem(row); + QTreeWidgetItem *cur_ti = eo_ui_->objectTree->topLevelItem(row); + ExportObjectTreeWidgetItem *eo_ti = dynamic_cast(cur_ti); - if (item) return item->data(0, Qt::UserRole).value(); + if (eo_ti) { + return eo_ti->entry(); + } return NULL; } void ExportObjectDialog::resetObjects() { - if (eo_protocoldata_resetfn_) eo_protocoldata_resetfn_(); + export_object_gui_reset_cb reset_cb = get_eo_reset_func(eo_); + if (reset_cb) + reset_cb(); + if (save_bt_) save_bt_->setEnabled(false); if (save_all_bt_) save_all_bt_->setEnabled(false); } @@ -178,9 +225,9 @@ void ExportObjectDialog::resetObjects() void ExportObjectDialog::show() { /* Data will be gathered via a tap callback */ - if (!registerTapListener(tap_name_, &export_object_list_, NULL, 0, + if (!registerTapListener(get_eo_tap_listener_name(eo_), &export_object_list_, NULL, 0, eo_reset, - tap_packet_, + get_eo_packet_func(eo_), NULL)) { return; } @@ -191,6 +238,8 @@ void ExportObjectDialog::show() for (int i = 0; i < eo_ui_->objectTree->columnCount(); i++) eo_ui_->objectTree->resizeColumnToContents(i); + eo_ui_->objectTree->setSortingEnabled(true); + eo_ui_->objectTree->sortByColumn(COL_PACKET, Qt::AscendingOrder); } void ExportObjectDialog::accept() @@ -217,7 +266,13 @@ void ExportObjectDialog::on_objectTree_currentItemChanged(QTreeWidgetItem *item, if (save_bt_) save_bt_->setEnabled(true); - export_object_entry_t *entry = item->data(0, Qt::UserRole).value(); + ExportObjectTreeWidgetItem *eo_ti = dynamic_cast(item); + + if (!eo_ti) { + return; + } + + export_object_entry_t *entry = eo_ti->entry(); if (entry && !file_closed_) { cf_goto_frame(cap_file_.capFile(), entry->pkt_num); } @@ -244,10 +299,15 @@ void ExportObjectDialog::saveCurrentEntry() QDir path(wsApp->lastOpenDir()); QString file_name; - if (!item) return; + ExportObjectTreeWidgetItem *eo_ti = dynamic_cast(item); + if (!eo_ti) { + return; + } - entry = item->data(0, Qt::UserRole).value(); - if (!entry) return; + entry = eo_ti->entry(); + if (!entry) { + return; + } file_name = QFileDialog::getSaveFileName(this, wsApp->windowTitleString(tr("Save Object As" UTF8_HORIZONTAL_ELLIPSIS)), path.filePath(entry->filename)); @@ -257,7 +317,6 @@ void ExportObjectDialog::saveCurrentEntry() } } -#define MAXFILELEN 255 void ExportObjectDialog::saveAllEntries() { int i; @@ -280,13 +339,17 @@ void ExportObjectDialog::saveAllEntries() save_in_dir.canonicalPath(), QFileDialog::ShowDirsOnly); - if (save_in_path.length() < 1 || save_in_path.length() > MAXFILELEN) return; + if (save_in_path.length() < 1 || save_in_path.length() > EXPORT_OBJECT_MAXFILELEN) return; for (i = 0; (item = eo_ui_->objectTree->topLevelItem(i)) != NULL; i++) { int count = 0; gchar *save_as_fullpath = NULL; - export_object_entry_t *entry = item->data(0, Qt::UserRole).value(); + ExportObjectTreeWidgetItem *eo_ti = dynamic_cast(item); + if (!eo_ti) { + continue; + } + export_object_entry_t *entry = eo_ti->entry(); if (!entry) continue; do { @@ -295,16 +358,16 @@ void ExportObjectDialog::saveAllEntries() g_free(save_as_fullpath); if (entry->filename) safe_filename = eo_massage_str(entry->filename, - MAXFILELEN - save_in_path.length(), count); + EXPORT_OBJECT_MAXFILELEN - save_in_path.length(), count); else { char generic_name[256]; const char *ext; - ext = ct2ext(entry->content_type); + ext = eo_ct2ext(entry->content_type); g_snprintf(generic_name, sizeof(generic_name), "object%u%s%s", entry->pkt_num, ext ? "." : "", ext ? ext : ""); safe_filename = eo_massage_str(generic_name, - MAXFILELEN - save_in_path.length(), count); + EXPORT_OBJECT_MAXFILELEN - save_in_path.length(), count); } save_as_fullpath = g_build_filename(save_in_path.toUtf8().constData(), safe_filename->str, NULL); diff --git a/ui/qt/export_object_dialog.h b/ui/qt/export_object_dialog.h index 9084017a80..e400a128b8 100644 --- a/ui/qt/export_object_dialog.h +++ b/ui/qt/export_object_dialog.h @@ -31,25 +31,22 @@ #include #include #include +#include -#include +#include #include "wireshark_dialog.h" -#include - class QTreeWidgetItem; class QAbstractButton; -Q_DECLARE_METATYPE(export_object_entry_t *) - namespace Ui { class ExportObjectDialog; } -struct _export_object_list_t { +typedef struct _export_object_list_gui_t { class ExportObjectDialog *eod; -}; +} export_object_list_gui_t; class ExportObjectDialog : public WiresharkDialog @@ -57,9 +54,7 @@ class ExportObjectDialog : public WiresharkDialog Q_OBJECT public: - enum ObjectType { Dicom, Http, Imf, Smb, Tftp }; - - explicit ExportObjectDialog(QWidget &parent, CaptureFile &cf, ObjectType object_type); + explicit ExportObjectDialog(QWidget &parent, CaptureFile &cf, register_eo_t* eo); ~ExportObjectDialog(); @@ -81,23 +76,14 @@ private: void saveCurrentEntry(); void saveAllEntries(); - /* When a protocol needs intermediate data structures to construct the - export objects, then it must specifiy 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 (*eo_protocoldata_reset_cb)(void); - Ui::ExportObjectDialog *eo_ui_; QPushButton *save_bt_; QPushButton *save_all_bt_; export_object_list_t export_object_list_; - const gchar *tap_name_; - const gchar *name_; - tap_packet_cb tap_packet_; - eo_protocoldata_reset_cb eo_protocoldata_resetfn_; + export_object_list_gui_t eo_gui_data_; + register_eo_t* eo_; }; #endif // EXPORT_OBJECT_DIALOG_H diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index 169fb943f4..8ed0f6a087 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "ui/commandline.h" @@ -54,6 +55,7 @@ #include "capture_interfaces_dialog.h" #endif #include "conversation_colorize_action.h" +#include "export_object_action.h" #include "display_filter_edit.h" #include "export_dissection_dialog.h" #include "file_set_dialog.h" @@ -347,6 +349,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus())); connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus())); connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus())); + connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initExportObjectsMenus())); connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry())); connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes())); @@ -1927,6 +1930,27 @@ void MainWindow::initConversationMenus() connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered())); } +void MainWindow::addExportObjectsMenuItem(gpointer data, gpointer user_data) +{ + register_eo_t *eo = (register_eo_t*)data; + MainWindow *window = (MainWindow*)user_data; + + ExportObjectAction *export_action = new ExportObjectAction(window->main_ui_->menuFileExportObjects, eo); + window->main_ui_->menuFileExportObjects->addAction(export_action); + + //initially disable until a file is loaded (then file signals will take over) + export_action->setEnabled(false); + + connect(&window->capture_file_, SIGNAL(captureFileOpened()), export_action, SLOT(captureFileOpened())); + connect(&window->capture_file_, SIGNAL(captureFileClosed()), export_action, SLOT(captureFileClosed())); + connect(export_action, SIGNAL(triggered()), window, SLOT(applyExportObject())); +} + +void MainWindow::initExportObjectsMenus() +{ + eo_iterate_tables(addExportObjectsMenuItem, this); +} + // Titlebar void MainWindow::setTitlebarForCaptureFile() { diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 5320fc7f13..cda9116ebb 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -292,6 +292,8 @@ private slots: void initViewColorizeMenu(); void initConversationMenus(); + static void addExportObjectsMenuItem(gpointer data, gpointer user_data); + void initExportObjectsMenus(); // in main_window_slots.cpp /** @@ -387,11 +389,6 @@ private slots: void on_actionFileExportAsPDML_triggered(); void on_actionFileExportAsJSON_triggered(); void on_actionFileExportPacketBytes_triggered(); - void on_actionFileExportObjectsDICOM_triggered(); - void on_actionFileExportObjectsHTTP_triggered(); - void on_actionFileExportObjectsIMF_triggered(); - void on_actionFileExportObjectsSMB_triggered(); - void on_actionFileExportObjectsTFTP_triggered(); void on_actionFilePrint_triggered(); void on_actionFileExportPDU_triggered(); @@ -489,6 +486,7 @@ private slots: void on_actionAnalyzePAFOrNotSelected_triggered(); void applyConversationFilter(); + void applyExportObject(); void on_actionAnalyzeEnabledProtocols_triggered(); void on_actionAnalyzeDecodeAs_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 7c4085d6af..a72b241d7e 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -178,11 +178,6 @@ Export Objects - - - - - @@ -1245,26 +1240,6 @@ As JSON… - - - &HTTP… - - - - - &IMF… - - - - - &DICOM… - - - - - &SMB… - - Description @@ -2076,11 +2051,6 @@ Show or hide the display filter toolbar - - - &TFTP - - Conversations diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 1b2ac37c11..dfdc8ac433 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -43,6 +43,7 @@ #include "ui/commandline.h" #include "epan/color_filters.h" +#include "epan/export_object.h" #include "wsutil/file_util.h" #include "wsutil/filesystem.h" @@ -102,6 +103,7 @@ #include "dissector_tables_dialog.h" #include "endpoint_dialog.h" #include "expert_info_dialog.h" +#include "export_object_action.h" #include "export_object_dialog.h" #include "export_pdu_dialog.h" #ifdef HAVE_EXTCAP @@ -1863,36 +1865,11 @@ void MainWindow::on_actionFileExportSSLSessionKeys_triggered() } } -void MainWindow::on_actionFileExportObjectsDICOM_triggered() -{ - new ExportObjectDialog(*this, capture_file_, ExportObjectDialog::Dicom); -} - void MainWindow::on_actionStatisticsHpfeeds_triggered() { openStatisticsTreeDialog("hpfeeds"); } -void MainWindow::on_actionFileExportObjectsHTTP_triggered() -{ - new ExportObjectDialog(*this, capture_file_, ExportObjectDialog::Http); -} - -void MainWindow::on_actionFileExportObjectsIMF_triggered() -{ - new ExportObjectDialog(*this, capture_file_, ExportObjectDialog::Imf); -} - -void MainWindow::on_actionFileExportObjectsSMB_triggered() -{ - new ExportObjectDialog(*this, capture_file_, ExportObjectDialog::Smb); -} - -void MainWindow::on_actionFileExportObjectsTFTP_triggered() -{ - new ExportObjectDialog(*this, capture_file_, ExportObjectDialog::Tftp); -} - void MainWindow::on_actionFilePrint_triggered() { PrintDialog pdlg(this, capture_file_.capFile()); @@ -2645,6 +2622,15 @@ void MainWindow::applyConversationFilter() } } +void MainWindow::applyExportObject() +{ + ExportObjectAction *export_action = qobject_cast(sender()); + if (!export_action) + return; + + new ExportObjectDialog(*this, capture_file_, export_action->exportObject()); +} + // XXX We could probably create the analyze and prepare actions // dynamically using FilterActions and consolidate the methods // below into one callback.