1 /* ringbuffer.c
2  * Routines for packet capture windows
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 /*
12  * <laurent.deniel@free.fr>
13  *
14  * Almost completely rewritten in order to:
15  *
16  * - be able to use a unlimited number of ringbuffer files
17  * - close the current file and open (truncating) the next file at switch
18  * - set the final file name once open (or reopen)
19  * - avoid the deletion of files that could not be truncated (can't arise now)
20  *   and do not erase empty files
21  *
22  * The idea behind that is to remove the limitation of the maximum # of
23  * ringbuffer files being less than the maximum # of open fd per process
24  * and to be able to reduce the amount of virtual memory usage (having only
25  * one file open at most) or the amount of file system usage (by truncating
26  * the files at switch and not the capture stop, and by closing them which
27  * makes possible their move or deletion after a switch).
28  *
29  */
30 
31 #include <config.h>
32 
33 #ifdef HAVE_LIBPCAP
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <time.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <glib.h>
41 
42 #include "wspcap.h"
43 
44 #include <glib.h>
45 
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 
50 #ifdef _WIN32
51 #include <wsutil/win32-utils.h>
52 #endif
53 
54 #include "ringbuffer.h"
55 #include <wsutil/file_util.h>
56 
57 #ifdef HAVE_ZLIB
58 #include <zlib.h>
59 #endif
60 
61 /* Ringbuffer file structure */
62 typedef struct _rb_file {
63   gchar         *name;
64 } rb_file;
65 
66 #define MAX_FILENAME_QUEUE  100
67 
68 /** Ringbuffer data structure */
69 typedef struct _ringbuf_data {
70   rb_file      *files;
71   guint         num_files;           /**< Number of ringbuffer files (1 to ...) */
72   guint         curr_file_num;       /**< Number of the current file (ever increasing) */
73   gchar        *fprefix;             /**< Filename prefix */
74   gchar        *fsuffix;             /**< Filename suffix */
75   gboolean      nametimenum;         /**< ...num_time... or ...time_num...   */
76   gboolean      unlimited;           /**< TRUE if unlimited number of files */
77 
78   int           fd;                  /**< Current ringbuffer file descriptor */
79   FILE         *pdh;
80   char         *io_buffer;              /**< The IO buffer used to write to the file */
81   gboolean      group_read_access;   /**< TRUE if files need to be opened with group read access */
82   FILE         *name_h;              /**< write names of completed files to this handle */
83   gchar        *compress_type;       /**< compress type */
84 
85   GMutex        mutex;               /**< mutex for oldnames */
86   gchar        *oldnames[MAX_FILENAME_QUEUE];       /**< filename list of pending to be deleted */
87 } ringbuf_data;
88 
89 static ringbuf_data rb_data;
90 
91 /*
92  * delete pending uncompressed pcap files.
93  */
CleanupOldCap(gchar * name)94 static void CleanupOldCap(gchar* name)
95 {
96   ws_statb64 statb;
97   size_t i;
98 
99   g_mutex_lock(&rb_data.mutex);
100 
101   /* Delete pending delete file */
102   for (i = 0; i < sizeof(rb_data.oldnames) / sizeof(rb_data.oldnames[0]); i++) {
103     if (rb_data.oldnames[i] != NULL) {
104       ws_unlink(rb_data.oldnames[i]);
105       if (ws_stat64(rb_data.oldnames[i], &statb) != 0) {
106         g_free(rb_data.oldnames[i]);
107         rb_data.oldnames[i] = NULL;
108       }
109     }
110   }
111 
112   if (name) {
113     /* push the current file to pending list if it failed to delete */
114     if (ws_stat64(name, &statb) == 0) {
115       for (i = 0; i < sizeof(rb_data.oldnames) / sizeof(rb_data.oldnames[0]); i++) {
116         if (rb_data.oldnames[i] == NULL) {
117           rb_data.oldnames[i] = g_strdup(name);
118           break;
119         }
120       }
121     }
122   }
123 
124   g_mutex_unlock(&rb_data.mutex);
125 }
126 
127 /*
128  * compress capture file
129  */
ringbuf_exec_compress(gchar * name)130 static int ringbuf_exec_compress(gchar* name)
131 {
132   guint8  *buffer = NULL;
133   gchar* outgz = NULL;
134   int  fd = -1;
135   ssize_t nread;
136   gboolean delete_org_file = TRUE;
137   gzFile fi = NULL;
138 
139   fd = ws_open(name, O_RDONLY | O_BINARY, 0000);
140   if (fd < 0) {
141     return -1;
142   }
143 
144   outgz = g_strdup_printf("%s.gz", name);
145   fi = gzopen(outgz, "wb");
146   g_free(outgz);
147   if (fi == NULL) {
148     ws_close(fd);
149     return -1;
150   }
151 
152 #define FS_READ_SIZE 65536
153   buffer = (guint8*)g_malloc(FS_READ_SIZE);
154   if (buffer == NULL) {
155     ws_close(fd);
156     gzclose(fi);
157     return -1;
158   }
159 
160   while ((nread = ws_read(fd, buffer, FS_READ_SIZE)) > 0) {
161     int n = gzwrite(fi, buffer, (unsigned int)nread);
162     if (n <= 0) {
163       /* mark compression as failed */
164       delete_org_file = FALSE;
165       break;
166     }
167   }
168   if (nread < 0) {
169     /* mark compression as failed */
170     delete_org_file = FALSE;
171   }
172   ws_close(fd);
173   gzclose(fi);
174   g_free(buffer);
175 
176   /* delete the original file only if compression succeeds */
177   if (delete_org_file) {
178     ws_unlink(name);
179     CleanupOldCap(name);
180   }
181   g_free(name);
182   return 0;
183 }
184 
185 /*
186  * thread to compress capture file
187  */
exec_compress_thread(void * arg)188 static void* exec_compress_thread(void* arg)
189 {
190   ringbuf_exec_compress((gchar*)arg);
191   return NULL;
192 }
193 
194 /*
195  * start a thread to compress capture file
196  */
ringbuf_start_compress_file(rb_file * rfile)197 static int ringbuf_start_compress_file(rb_file* rfile)
198 {
199   gchar* name = g_strdup(rfile->name);
200   g_thread_new("exec_compress", &exec_compress_thread, name);
201   return 0;
202 }
203 
204 /*
205  * create the next filename and open a new binary file with that name
206  */
ringbuf_open_file(rb_file * rfile,int * err)207 static int ringbuf_open_file(rb_file *rfile, int *err)
208 {
209   char    filenum[5+1];
210   char    timestr[14+1];
211   time_t  current_time;
212   struct tm *tm;
213 
214   if (rfile->name != NULL) {
215     if (rb_data.unlimited == FALSE) {
216       /* remove old file (if any, so ignore error) */
217       ws_unlink(rfile->name);
218     }
219     else if (rb_data.compress_type != NULL && strcmp(rb_data.compress_type, "gzip") == 0) {
220       ringbuf_start_compress_file(rfile);
221     }
222     g_free(rfile->name);
223   }
224 
225 #ifdef _WIN32
226   _tzset();
227 #endif
228   current_time = time(NULL);
229 
230   g_snprintf(filenum, sizeof(filenum), "%05u", (rb_data.curr_file_num + 1) % RINGBUFFER_MAX_NUM_FILES);
231   tm = localtime(&current_time);
232   if (tm != NULL)
233     strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", tm);
234   else
235     (void) g_strlcpy(timestr, "196912312359", sizeof(timestr)); /* second before the Epoch */
236   if (rb_data.nametimenum) {
237     rfile->name = g_strconcat(rb_data.fprefix, "_", timestr, "_", filenum, rb_data.fsuffix, NULL);
238   } else {
239     rfile->name = g_strconcat(rb_data.fprefix, "_", filenum, "_", timestr, rb_data.fsuffix, NULL);
240   }
241 
242   if (rfile->name == NULL) {
243     if (err != NULL)
244       *err = ENOMEM;
245     return -1;
246   }
247 
248   rb_data.fd = ws_open(rfile->name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
249                             rb_data.group_read_access ? 0640 : 0600);
250 
251   if (rb_data.fd == -1 && err != NULL) {
252     *err = errno;
253   }
254 
255   return rb_data.fd;
256 }
257 
258 /*
259  * Initialize the ringbuffer data structures
260  */
261 int
ringbuf_init(const char * capfile_name,guint num_files,gboolean group_read_access,gchar * compress_type,gboolean has_nametimenum)262 ringbuf_init(const char *capfile_name, guint num_files, gboolean group_read_access,
263              gchar *compress_type, gboolean has_nametimenum)
264 {
265   unsigned int i;
266   char        *pfx, *last_pathsep;
267   gchar       *save_file;
268 
269   rb_data.files = NULL;
270   rb_data.curr_file_num = 0;
271   rb_data.fprefix = NULL;
272   rb_data.fsuffix = NULL;
273   rb_data.nametimenum = has_nametimenum;
274   rb_data.unlimited = FALSE;
275   rb_data.fd = -1;
276   rb_data.pdh = NULL;
277   rb_data.io_buffer = NULL;
278   rb_data.group_read_access = group_read_access;
279   rb_data.name_h = NULL;
280   rb_data.compress_type = compress_type;
281   g_mutex_init(&rb_data.mutex);
282 
283   /* just to be sure ... */
284   if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
285     rb_data.num_files = num_files;
286   } else {
287     rb_data.num_files = RINGBUFFER_MAX_NUM_FILES;
288   }
289 
290   /* Check file name */
291   if (capfile_name == NULL) {
292     /* ringbuffer does not work with temporary files! */
293     return -1;
294   }
295 
296   /* set file name prefix/suffix */
297 
298   save_file = g_strdup(capfile_name);
299   last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
300   pfx = strrchr(save_file,'.');
301   if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
302     /* The pathname has a "." in it, and it's in the last component
303        of the pathname (because there is either only one component,
304        i.e. last_pathsep is null as there are no path separators,
305        or the "." is after the path separator before the last
306        component.
307 
308        Treat it as a separator between the rest of the file name and
309        the file name suffix, and arrange that the names given to the
310        ring buffer files have the specified suffix, i.e. put the
311        changing part of the name *before* the suffix. */
312     pfx[0] = '\0';
313     rb_data.fprefix = g_strdup(save_file);
314     pfx[0] = '.'; /* restore capfile_name */
315     rb_data.fsuffix = g_strdup(pfx);
316   } else {
317     /* Either there's no "." in the pathname, or it's in a directory
318        component, so the last component has no suffix. */
319     rb_data.fprefix = g_strdup(save_file);
320     rb_data.fsuffix = NULL;
321   }
322   g_free(save_file);
323   save_file = NULL;
324 
325   /* allocate rb_file structures (only one if unlimited since there is no
326      need to save all file names in that case) */
327 
328   if (num_files == RINGBUFFER_UNLIMITED_FILES) {
329     rb_data.unlimited = TRUE;
330     rb_data.num_files = 1;
331   }
332 
333   rb_data.files = g_new(rb_file, rb_data.num_files);
334   if (rb_data.files == NULL) {
335     return -1;
336   }
337 
338   for (i=0; i < rb_data.num_files; i++) {
339     rb_data.files[i].name = NULL;
340   }
341 
342   /* create the first file */
343   if (ringbuf_open_file(&rb_data.files[0], NULL) == -1) {
344     ringbuf_error_cleanup();
345     return -1;
346   }
347 
348   return rb_data.fd;
349 }
350 
351 /*
352  * Set name of file to which to print ringbuffer file names.
353  */
354 gboolean
ringbuf_set_print_name(gchar * name,int * err)355 ringbuf_set_print_name(gchar *name, int *err)
356 {
357   if (rb_data.name_h != NULL) {
358     if (EOF == fclose(rb_data.name_h)) {
359       if (err != NULL) {
360         *err = errno;
361       }
362       return FALSE;
363     }
364   }
365   if (!strcmp(name, "-") || !strcmp(name, "stdout")) {
366     rb_data.name_h = stdout;
367   } else if (!strcmp(name, "stderr")) {
368     rb_data.name_h = stderr;
369   } else {
370     if (NULL == (rb_data.name_h = ws_fopen(name, "wt"))) {
371       if (err != NULL) {
372         *err = errno;
373       }
374       return FALSE;
375     }
376   }
377   return TRUE;
378 }
379 
380 /*
381  * Whether the ringbuf filenames are ready.
382  * (Whether ringbuf_init is called and ringbuf_free is not called.)
383  */
ringbuf_is_initialized(void)384 gboolean ringbuf_is_initialized(void)
385 {
386   return rb_data.files != NULL;
387 }
388 
ringbuf_current_filename(void)389 const gchar *ringbuf_current_filename(void)
390 {
391   return rb_data.files[rb_data.curr_file_num % rb_data.num_files].name;
392 }
393 
394 /*
395  * Calls ws_fdopen() for the current ringbuffer file
396  */
397 FILE *
ringbuf_init_libpcap_fdopen(int * err)398 ringbuf_init_libpcap_fdopen(int *err)
399 {
400   rb_data.pdh = ws_fdopen(rb_data.fd, "wb");
401   if (rb_data.pdh == NULL) {
402     if (err != NULL) {
403       *err = errno;
404     }
405   } else {
406     size_t buffsize = IO_BUF_SIZE;
407 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
408     ws_statb64 statb;
409 
410     if (ws_fstat64(rb_data.fd, &statb) == 0) {
411       if (statb.st_blksize > IO_BUF_SIZE) {
412         buffsize = statb.st_blksize;
413       }
414     }
415 #endif
416     /* Increase the size of the IO buffer */
417     rb_data.io_buffer = (char *)g_realloc(rb_data.io_buffer, buffsize);
418     setvbuf(rb_data.pdh, rb_data.io_buffer, _IOFBF, buffsize);
419   }
420 
421   return rb_data.pdh;
422 }
423 
424 /*
425  * Switches to the next ringbuffer file
426  */
427 gboolean
ringbuf_switch_file(FILE ** pdh,gchar ** save_file,int * save_file_fd,int * err)428 ringbuf_switch_file(FILE **pdh, gchar **save_file, int *save_file_fd, int *err)
429 {
430   int     next_file_index;
431   rb_file *next_rfile = NULL;
432 
433   /* close current file */
434 
435   if (fclose(rb_data.pdh) == EOF) {
436     if (err != NULL) {
437       *err = errno;
438     }
439     ws_close(rb_data.fd);  /* XXX - the above should have closed this already */
440     rb_data.pdh = NULL;    /* it's still closed, we just got an error while closing */
441     rb_data.fd = -1;
442     g_free(rb_data.io_buffer);
443     rb_data.io_buffer = NULL;
444     return FALSE;
445   }
446 
447   rb_data.pdh = NULL;
448   rb_data.fd  = -1;
449 
450   if (rb_data.name_h != NULL) {
451     fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename());
452     fflush(rb_data.name_h);
453   }
454 
455   /* get the next file number and open it */
456 
457   rb_data.curr_file_num++ /* = next_file_num*/;
458   next_file_index = (rb_data.curr_file_num) % rb_data.num_files;
459   next_rfile = &rb_data.files[next_file_index];
460 
461   if (ringbuf_open_file(next_rfile, err) == -1) {
462     return FALSE;
463   }
464 
465   if (ringbuf_init_libpcap_fdopen(err) == NULL) {
466     return FALSE;
467   }
468 
469   /* switch to the new file */
470   *save_file = next_rfile->name;
471   *save_file_fd = rb_data.fd;
472   (*pdh) = rb_data.pdh;
473 
474   return TRUE;
475 }
476 
477 /*
478  * Calls fclose() for the current ringbuffer file
479  */
480 gboolean
ringbuf_libpcap_dump_close(gchar ** save_file,int * err)481 ringbuf_libpcap_dump_close(gchar **save_file, int *err)
482 {
483   gboolean  ret_val = TRUE;
484 
485   /* close current file, if it's open */
486   if (rb_data.pdh != NULL) {
487     if (fclose(rb_data.pdh) == EOF) {
488       if (err != NULL) {
489         *err = errno;
490       }
491       ws_close(rb_data.fd);
492       ret_val = FALSE;
493     }
494     rb_data.pdh = NULL;
495     rb_data.fd  = -1;
496     g_free(rb_data.io_buffer);
497     rb_data.io_buffer = NULL;
498 
499   }
500 
501   if (rb_data.name_h != NULL) {
502     fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename());
503     fflush(rb_data.name_h);
504 
505     if (EOF == fclose(rb_data.name_h)) {
506       /* Can't really do much about this, can we? */
507     }
508   }
509 
510   /* set the save file name to the current file */
511   *save_file = rb_data.files[rb_data.curr_file_num % rb_data.num_files].name;
512   return ret_val;
513 }
514 
515 /*
516  * Frees all memory allocated by the ringbuffer
517  */
518 void
ringbuf_free(void)519 ringbuf_free(void)
520 {
521   unsigned int i;
522 
523   if (rb_data.files != NULL) {
524     for (i=0; i < rb_data.num_files; i++) {
525       if (rb_data.files[i].name != NULL) {
526         g_free(rb_data.files[i].name);
527         rb_data.files[i].name = NULL;
528       }
529     }
530     g_free(rb_data.files);
531     rb_data.files = NULL;
532   }
533   if (rb_data.fprefix != NULL) {
534     g_free(rb_data.fprefix);
535     rb_data.fprefix = NULL;
536   }
537   if (rb_data.fsuffix != NULL) {
538     g_free(rb_data.fsuffix);
539     rb_data.fsuffix = NULL;
540   }
541 
542   CleanupOldCap(NULL);
543 }
544 
545 /*
546  * Frees all memory allocated by the ringbuffer
547  */
548 void
ringbuf_error_cleanup(void)549 ringbuf_error_cleanup(void)
550 {
551   unsigned int i;
552 
553   /* try to close via wtap */
554   if (rb_data.pdh != NULL) {
555     if (fclose(rb_data.pdh) == 0) {
556       rb_data.fd = -1;
557     }
558     rb_data.pdh = NULL;
559   }
560 
561   /* close directly if still open */
562   if (rb_data.fd != -1) {
563     ws_close(rb_data.fd);
564     rb_data.fd = -1;
565   }
566 
567   if (rb_data.files != NULL) {
568     for (i=0; i < rb_data.num_files; i++) {
569       if (rb_data.files[i].name != NULL) {
570         ws_unlink(rb_data.files[i].name);
571       }
572     }
573   }
574   g_free(rb_data.io_buffer);
575   rb_data.io_buffer = NULL;
576 
577   if (rb_data.name_h != NULL) {
578     if (EOF == fclose(rb_data.name_h)) {
579       /* Can't really do much about this, can we? */
580     }
581   }
582 
583   /* free the memory */
584   ringbuf_free();
585 }
586 
587 #endif /* HAVE_LIBPCAP */
588 
589 /*
590  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
591  *
592  * Local Variables:
593  * c-basic-offset: 2
594  * tab-width: 8
595  * indent-tabs-mode: nil
596  * End:
597  *
598  * ex: set shiftwidth=2 tabstop=8 expandtab:
599  * :indentSize=2:tabSize=8:noTabs=true:
600  */
601