packet-smb2: fix lease epoch fields
[metze/wireshark/wip.git] / ui / export_object_smb.c
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
6  *
7  * Copyright 2010, David Perez & Jose Pico from TADDONG S.L.
8  *
9  * $Id$
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
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.
19  *
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.
24  *
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.
29  */
30
31 #include "config.h"
32
33 #include <glib.h>
34
35 #include <epan/packet.h>
36 #include <epan/dissectors/packet-smb.h>
37 #include <epan/dissectors/packet-smb2.h>
38 #include <epan/tap.h>
39
40 #include "export_object.h"
41
42
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_.- /\\{}[]=()&%$!,;.+&%$~#@"
50
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}
57 };
58
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}
66 };
67
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}
75 };
76
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 */
80
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 ;
92
93 /* This is the GSList that will contain all the files that we are tracking */
94 static GSList    *GSL_active_files = NULL;
95
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;
102
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.
113 */
114 static void
115 insert_chunk(active_file   *file, export_object_entry_t *entry, const smb_eo_t *eo_info)
116 {
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;
127
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;
145                 }
146         }
147         file->file_length = MAX(file->file_length, chunk_end_offset+1);
148
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;
156                 }
157                 /* 2. data chunk overlaps the first part of free_chunk */
158                 /* -> free chunk shrinks from the beginning */
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;
163                 }
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;
174                         }
175                         i--;
176                         continue;
177                 }
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;
188                 }
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;
195                 }
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;
200                 }
201         }
202
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) {
208                         /*
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.
214                         */
215                         entry->payload_data = NULL;
216                 } else {
217                         entry->payload_data = (guint8 *)g_try_malloc((gsize)calculated_size);
218                         entry->payload_len  = calculated_size;
219                 }
220                 if (!entry->payload_data) {
221                         /* Memory error */
222                         file->is_out_of_memory = TRUE;
223                 }
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) {
230                                 /*
231                                 * As for g_try_malloc(), so for
232                                 * g_try_realloc().
233                                 */
234                                 dest_memory_addr = NULL;
235                         } else {
236                                 dest_memory_addr = g_try_realloc(
237                                 entry->payload_data,
238                                 (gsize)calculated_size);
239                         }
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;
251                         }
252                 }
253         }
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                 memmove(dest_memory_addr, eo_info->payload_data, eo_info->payload_len);
258         }
259 }
260
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)
264 {
265         int          i, row, last;
266         active_file *in_list_file;
267
268         row  = -1;
269         last = g_slist_length(GSL_active_files_p) - 1;
270
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;
285                 }
286         }
287
288         return row;
289 }
290
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)
294 {
295 export_object_list_t   *object_list = (export_object_list_t *)tapdata;
296 const smb_eo_t         *eo_info     = (const smb_eo_t *)data;
297
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;
307
308 gchar              *aux_smb_fid_type_string;
309
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));
314
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;
328                 }
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));
333
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;
345                 }
346         }
347
348
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);
354
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;
370
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,'?'));
373
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);
377                 }
378
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;
391                         }
392
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);
400                 }
401
402                 object_list_add_entry(object_list, entry);
403                 GSL_active_files = g_slist_append(GSL_active_files, new_file);
404         }
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);
410
411                 insert_chunk(current_file, current_entry, eo_info);
412
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);
430                 }
431         }
432
433         return TRUE; /* State changed - window should be redrawn */
434 }
435
436
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)
442 {
443 int          i, last;
444 active_file *in_list_file;
445
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;
455                         }
456                         g_free(in_list_file);
457                 }
458                 g_slist_free(GSL_active_files);
459                 GSL_active_files = NULL;
460         }
461 }
462
463 /*
464  * Editor modelines
465  *
466  * Local Variables:
467  * c-basic-offset: 4
468  * tab-width: 8
469  * indent-tabs-mode: nil
470  * End:
471  *
472  * ex: set shiftwidth=4 tabstop=8 expandtab:
473  * :indentSize=4:tabSize=8:noTabs=true:
474  */