Witness: enum witness_notifyResponse_type
[wireshark-wip.git] / ui / export_object_smb.c
blobdec3de9eca91b021354bfcb5cc1fc4b705ea50c3
1 /* export_object_smb.c
2 * Routines for tracking & saving objects (files) found in SMB streams
3 * See also: export_object.c / export_object.h for common code
4 * Initial file, prototypes and general structure initially copied
5 * from export_object_http.c
7 * Copyright 2010, David Perez & Jose Pico from TADDONG S.L.
9 * $Id$
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
28 * USA.
31 #include "config.h"
33 #include <glib.h>
35 #include <epan/packet.h>
36 #include <epan/dissectors/packet-smb.h>
37 #include <epan/dissectors/packet-smb2.h>
38 #include <epan/tap.h>
40 #include "export_object.h"
43 /* These flags show what kind of data the object contains
44 (designed to be or'ed) */
45 #define SMB_EO_CONTAINS_NOTHING 0x00
46 #define SMB_EO_CONTAINS_READS 0x01
47 #define SMB_EO_CONTAINS_WRITES 0x02
48 #define SMB_EO_CONTAINS_READSANDWRITES 0x03
49 #define LEGAL_FILENAME_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.- /\\{}[]=()&%$!,;.+&%$~#@"
51 static const value_string smb_eo_contains_string[] = {
52 {SMB_EO_CONTAINS_NOTHING, "" },
53 {SMB_EO_CONTAINS_READS, "R" },
54 {SMB_EO_CONTAINS_WRITES, "W" },
55 {SMB_EO_CONTAINS_READSANDWRITES, "R&W"},
56 {0, NULL}
59 /* Strings that describes the SMB object type */
60 static const value_string smb_fid_types[] = {
61 {SMB_FID_TYPE_UNKNOWN,"UNKNOWN"},
62 {SMB_FID_TYPE_FILE,"FILE"},
63 {SMB_FID_TYPE_DIR,"DIRECTORY (Not Implemented)"},
64 {SMB_FID_TYPE_PIPE,"PIPE (Not Implemented)"},
65 {0, NULL}
68 static const value_string smb2_fid_types[] = {
69 {SMB2_FID_TYPE_UNKNOWN,"UNKNOWN"},
70 {SMB2_FID_TYPE_FILE,"FILE"},
71 {SMB2_FID_TYPE_DIR,"DIRECTORY (Not Implemented)"},
72 {SMB2_FID_TYPE_PIPE,"PIPE (Not Implemented)"},
73 {SMB2_FID_TYPE_OTHER,"OTHER (Not Implemented)"},
74 {0, NULL}
77 /* This struct contains the relationship between
78 the row# in the export_object window and the file being captured;
79 the row# in this GSList will match the row# in the entry list */
81 typedef struct _active_file {
82 guint16 tid, uid, fid;
83 guint64 file_length; /* The last free reported offset */
84 /* We treat it as the file length */
85 guint64 data_gathered; /* The actual total of data gathered */
86 guint8 flag_contains; /* What kind of data it contains */
87 GSList *free_chunk_list; /* A list of virtual "holes" in the */
88 /* file stream stored in memory */
89 gboolean is_out_of_memory; /* TRUE if we cannot allocate memory */
90 /* memory for this file */
91 } active_file ;
93 /* This is the GSList that will contain all the files that we are tracking */
94 static GSList *GSL_active_files = NULL;
96 /* We define a free chunk in a file as an start offset and end offset
97 Consider a free chunk as a "hole" in a file that we are capturing */
98 typedef struct _free_chunk {
99 guint64 start_offset;
100 guint64 end_offset;
101 } free_chunk;
103 /* insert_chunk function will recalculate the free_chunk_list, the data_size,
104 the end_of_file, and the data_gathered as appropriate.
105 It will also insert the data chunk that is coming in the right
106 place of the file in memory.
107 HINTS:
108 file->data_gathered contains the real data gathered independently
109 from the file length
110 file->file_length contains the length of the file in memory, i.e.,
111 the last offset captured. In most cases, the real
112 file length would be different.
114 static void
115 insert_chunk(active_file *file, export_object_entry_t *entry, const smb_eo_t *eo_info)
117 gint nfreechunks = g_slist_length(file->free_chunk_list);
118 gint i;
119 free_chunk *current_free_chunk;
120 free_chunk *new_free_chunk;
121 guint64 chunk_offset = eo_info->smb_file_offset;
122 guint64 chunk_length = eo_info->payload_len;
123 guint64 chunk_end_offset = chunk_offset + chunk_length-1;
124 /* Size of file in memory */
125 guint64 calculated_size = chunk_offset + chunk_length;
126 gpointer dest_memory_addr;
128 /* Let's recalculate the file length and data gathered */
129 if ((file->data_gathered == 0) && (nfreechunks == 0)) {
130 /* If this is the first entry for this file, we first
131 create an initial free chunk */
132 new_free_chunk = (free_chunk *)g_malloc(sizeof(free_chunk));
133 new_free_chunk->start_offset = 0;
134 new_free_chunk->end_offset = MAX(file->file_length, chunk_end_offset+1) - 1;
135 file->free_chunk_list = NULL;
136 file->free_chunk_list = g_slist_append(file->free_chunk_list, new_free_chunk);
137 nfreechunks += 1;
138 } else {
139 if (chunk_end_offset > file->file_length-1) {
140 new_free_chunk = (free_chunk *)g_malloc(sizeof(free_chunk));
141 new_free_chunk->start_offset = file->file_length;
142 new_free_chunk->end_offset = chunk_end_offset;
143 file->free_chunk_list = g_slist_append(file->free_chunk_list, new_free_chunk);
144 nfreechunks += 1;
147 file->file_length = MAX(file->file_length, chunk_end_offset+1);
149 /* Recalculate each free chunk according with the incoming data chunk */
150 for (i=0; i<nfreechunks; i++) {
151 current_free_chunk = (free_chunk *)g_slist_nth_data(file->free_chunk_list, i);
152 /* 1. data chunk before the free chunk? */
153 /* -> free chunk is not altered and no new data gathered */
154 if (chunk_end_offset<current_free_chunk->start_offset) {
155 continue;
157 /* 2. data chunk overlaps the first part of free_chunk */
158 /* -> free chunk shrinks from the beggining */
159 if (chunk_offset<=current_free_chunk->start_offset && chunk_end_offset>=current_free_chunk->start_offset && chunk_end_offset<current_free_chunk->end_offset) {
160 file->data_gathered += chunk_end_offset-current_free_chunk->start_offset+1;
161 current_free_chunk->start_offset=chunk_end_offset+1;
162 continue;
164 /* 3. data chunk overlaps completely the free chunk */
165 /* -> free chunk is removed */
166 if (chunk_offset<=current_free_chunk->start_offset && chunk_end_offset>=current_free_chunk->end_offset) {
167 file->data_gathered += current_free_chunk->end_offset-current_free_chunk->start_offset+1;
168 file->free_chunk_list = g_slist_remove(file->free_chunk_list, current_free_chunk);
169 nfreechunks -= 1;
170 if (nfreechunks == 0) { /* The free chunk list is empty */
171 g_slist_free(file->free_chunk_list);
172 file->free_chunk_list = NULL;
173 break;
175 i--;
176 continue;
178 /* 4. data chunk is inside the free chunk */
179 /* -> free chunk is splitted into two */
180 if (chunk_offset>current_free_chunk->start_offset && chunk_end_offset<current_free_chunk->end_offset) {
181 new_free_chunk = (free_chunk *)g_malloc(sizeof(free_chunk));
182 new_free_chunk->start_offset = chunk_end_offset + 1;
183 new_free_chunk->end_offset = current_free_chunk->end_offset;
184 current_free_chunk->end_offset = chunk_offset-1;
185 file->free_chunk_list = g_slist_insert(file->free_chunk_list, new_free_chunk, i + 1);
186 file->data_gathered += chunk_length;
187 continue;
189 /* 5.- data chunk overlaps the end part of free chunk */
190 /* -> free chunk shrinks from the end */
191 if (chunk_offset>current_free_chunk->start_offset && chunk_offset<=current_free_chunk->end_offset && chunk_end_offset>=current_free_chunk->end_offset) {
192 file->data_gathered += current_free_chunk->end_offset-chunk_offset+1;
193 current_free_chunk->end_offset = chunk_offset-1;
194 continue;
196 /* 6.- data chunk is after the free chunk */
197 /* -> free chunk is not altered and no new data gathered */
198 if (chunk_offset>current_free_chunk->end_offset) {
199 continue;
203 /* Now, let's insert the data chunk into memory
204 ...first, we shall be able to allocate the memory */
205 if (!entry->payload_data) {
206 /* This is a New file */
207 if (calculated_size > G_MAXSIZE) {
209 * The argument to g_try_malloc() is
210 * a gsize, the maximum value of which is
211 * G_MAXSIZE. If the calculated size is
212 * bigger than that, we just say the attempt
213 * to allocate memory failed.
215 entry->payload_data = NULL;
216 } else {
217 entry->payload_data = (guint8 *)g_try_malloc((gsize)calculated_size);
218 entry->payload_len = calculated_size;
220 if (!entry->payload_data) {
221 /* Memory error */
222 file->is_out_of_memory = TRUE;
224 } else {
225 /* This is an existing file in memory */
226 if (calculated_size > (guint64) entry->payload_len &&
227 !file->is_out_of_memory) {
228 /* We need more memory */
229 if (calculated_size > G_MAXSIZE) {
231 * As for g_try_malloc(), so for
232 * g_try_realloc().
234 dest_memory_addr = NULL;
235 } else {
236 dest_memory_addr = g_try_realloc(
237 entry->payload_data,
238 (gsize)calculated_size);
240 if (!dest_memory_addr) {
241 /* Memory error */
242 file->is_out_of_memory = TRUE;
243 /* We don't have memory for this file.
244 Free the current file content from memory */
245 g_free(entry->payload_data);
246 entry->payload_data = NULL;
247 entry->payload_len = 0;
248 } else {
249 entry->payload_data = (guint8 *)dest_memory_addr;
250 entry->payload_len = calculated_size;
254 /* ...then, put the chunk of the file in the right place */
255 if (!file->is_out_of_memory) {
256 dest_memory_addr = entry->payload_data + chunk_offset;
257 g_memmove(dest_memory_addr, eo_info->payload_data, eo_info->payload_len);
261 /* We use this function to obtain the index in the GSL of a given file */
262 static int
263 find_incoming_file(GSList *GSL_active_files_p, active_file *incoming_file)
265 int i, row, last;
266 active_file *in_list_file;
268 row = -1;
269 last = g_slist_length(GSL_active_files_p) - 1;
271 /* We lookup in reverse order because it is more likely that the file
272 is one of the latest */
273 for (i=last; i>=0; i--) {
274 in_list_file = (active_file *)g_slist_nth_data(GSL_active_files_p, i);
275 /* The best-working criteria of two identical files is that the file
276 that is the same of the file that we are analyzing is the last one
277 in the list that has the same tid and the same fid */
278 /* note that we have excluded in_list_file->uid == incoming_file->uid
279 from the comparison, because a file can be opened by different
280 SMB users and it is still the same file */
281 if (in_list_file->tid == incoming_file->tid &&
282 in_list_file->fid == incoming_file->fid) {
283 row = i;
284 break;
288 return row;
291 /* This is the function answering to the registered tap listener call */
292 gboolean
293 eo_smb_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data)
295 export_object_list_t *object_list = (export_object_list_t *)tapdata;
296 const smb_eo_t *eo_info = (const smb_eo_t *)data;
298 export_object_entry_t *entry;
299 export_object_entry_t *current_entry;
300 active_file incoming_file;
301 gint active_row;
302 active_file *new_file;
303 active_file *current_file;
304 guint8 contains;
305 gboolean is_supported_filetype;
306 gfloat percent;
308 gchar *aux_smb_fid_type_string;
310 if (eo_info->smbversion==1) {
311 /* Is this an eo_smb supported file_type? (right now we only support FILE) */
312 is_supported_filetype = (eo_info->fid_type == SMB_FID_TYPE_FILE);
313 aux_smb_fid_type_string=g_strdup(try_val_to_str(eo_info->fid_type, smb_fid_types));
315 /* What kind of data this packet contains? */
316 switch(eo_info->cmd) {
317 case SMB_COM_READ_ANDX:
318 case SMB_COM_READ:
319 contains = SMB_EO_CONTAINS_READS;
320 break;
321 case SMB_COM_WRITE_ANDX:
322 case SMB_COM_WRITE:
323 contains = SMB_EO_CONTAINS_WRITES;
324 break;
325 default:
326 contains = SMB_EO_CONTAINS_NOTHING;
327 break;
329 } else {
330 /* Is this an eo_smb supported file_type? (right now we only support FILE) */
331 is_supported_filetype = (eo_info->fid_type == SMB2_FID_TYPE_FILE );
332 aux_smb_fid_type_string=g_strdup(try_val_to_str(eo_info->fid_type, smb2_fid_types));
334 /* What kind of data this packet contains? */
335 switch(eo_info->cmd) {
336 case SMB2_COM_READ:
337 contains = SMB_EO_CONTAINS_READS;
338 break;
339 case SMB2_COM_WRITE:
340 contains = SMB_EO_CONTAINS_WRITES;
341 break;
342 default:
343 contains = SMB_EO_CONTAINS_NOTHING;
344 break;
349 /* Is this data from an already tracked file or not? */
350 incoming_file.tid = eo_info->tid;
351 incoming_file.uid = eo_info->uid;
352 incoming_file.fid = eo_info->fid;
353 active_row = find_incoming_file(GSL_active_files, &incoming_file);
355 if (active_row == -1) { /* This is a new-tracked file */
356 /* Construct the entry in the list of active files */
357 entry = (export_object_entry_t *)g_malloc(sizeof(export_object_entry_t));
358 entry->payload_data = NULL;
359 entry->payload_len = 0;
360 new_file = (active_file *)g_malloc(sizeof(active_file));
361 new_file->tid = incoming_file.tid;
362 new_file->uid = incoming_file.uid;
363 new_file->fid = incoming_file.fid;
364 new_file->file_length = eo_info->end_of_file;
365 new_file->flag_contains = contains;
366 new_file->free_chunk_list = NULL;
367 new_file->data_gathered = 0;
368 new_file->is_out_of_memory = FALSE;
369 entry->pkt_num = pinfo->fd->num;
371 entry->hostname=g_filename_display_name(g_strcanon(eo_info->hostname,LEGAL_FILENAME_CHARS,'?'));
372 entry->filename=g_filename_display_name(g_strcanon(eo_info->filename,LEGAL_FILENAME_CHARS,'?'));
374 /* Insert the first chunk in the chunk list of this file */
375 if (is_supported_filetype) {
376 insert_chunk(new_file, entry, eo_info);
379 if (new_file->is_out_of_memory) {
380 entry->content_type =
381 g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]",
382 aux_smb_fid_type_string,
383 new_file->data_gathered,
384 new_file->file_length,
385 try_val_to_str(contains, smb_eo_contains_string));
386 } else {
387 if (new_file->file_length > 0) {
388 percent = (gfloat) (100*new_file->data_gathered/new_file->file_length);
389 } else {
390 percent = 0.0f;
393 entry->content_type =
394 g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]",
395 aux_smb_fid_type_string,
396 new_file->data_gathered,
397 new_file->file_length,
398 try_val_to_str(contains, smb_eo_contains_string),
399 percent);
402 object_list_add_entry(object_list, entry);
403 GSL_active_files = g_slist_append(GSL_active_files, new_file);
405 else if (is_supported_filetype) {
406 current_file = (active_file *)g_slist_nth_data(GSL_active_files, active_row);
407 /* Recalculate the current file flags */
408 current_file->flag_contains = current_file->flag_contains|contains;
409 current_entry = object_list_get_entry(object_list, active_row);
411 insert_chunk(current_file, current_entry, eo_info);
413 /* Modify the current_entry object_type string */
414 if (current_file->is_out_of_memory) {
415 current_entry->content_type =
416 g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]",
417 aux_smb_fid_type_string,
418 current_file->data_gathered,
419 current_file->file_length,
420 try_val_to_str(current_file->flag_contains, smb_eo_contains_string));
421 } else {
422 percent = (gfloat) (100*current_file->data_gathered/current_file->file_length);
423 current_entry->content_type =
424 g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]",
425 aux_smb_fid_type_string,
426 current_file->data_gathered,
427 current_file->file_length,
428 try_val_to_str(current_file->flag_contains, smb_eo_contains_string),
429 percent);
433 return TRUE; /* State changed - window should be redrawn */
437 /* This is the eo_protocoldata_reset function that is used in the export_object module
438 to cleanup any previous private data of the export object functionality before perform
439 the eo_reset function or when the window closes */
440 void
441 eo_smb_cleanup(void)
443 int i, last;
444 active_file *in_list_file;
446 /* Free any previous data structures used in previous invocation to the
447 export_object_smb function */
448 last = g_slist_length(GSL_active_files);
449 if (GSL_active_files) {
450 for (i=last-1; i>=0; i--) {
451 in_list_file = (active_file *)g_slist_nth_data(GSL_active_files, i);
452 if (in_list_file->free_chunk_list) {
453 g_slist_free(in_list_file->free_chunk_list);
454 in_list_file->free_chunk_list = NULL;
456 g_free(in_list_file);
458 g_slist_free(GSL_active_files);
459 GSL_active_files = NULL;
464 * Editor modelines
466 * Local Variables:
467 * c-basic-offset: 4
468 * tab-width: 8
469 * indent-tabs-mode: nil
470 * End:
472 * ex: set shiftwidth=4 tabstop=8 expandtab:
473 * :indentSize=4:tabSize=8:noTabs=true: