Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/source-pcap-file-directory-helper.c
Line
Count
Source
1
/* Copyright (C) 2007-2016 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Danny Browning <danny.browning@protectwise.com>
22
 *
23
 * Helper methods for directory based packet acquisition
24
 */
25
26
#include "source-pcap-file-directory-helper.h"
27
#include "suricata.h"
28
#include "runmode-unix-socket.h"
29
#include "util-mem.h"
30
#include "util-time.h"
31
#include "util-path.h"
32
#include "source-pcap-file.h"
33
34
static void GetTime(struct timespec *tm);
35
static void CopyTime(struct timespec *from, struct timespec *to);
36
static int CompareTimes(struct timespec *left, struct timespec *right);
37
static TmEcode PcapRunStatus(PcapFileDirectoryVars *);
38
static TmEcode PcapDirectoryFailure(PcapFileDirectoryVars *ptv);
39
static TmEcode PcapDirectoryDone(PcapFileDirectoryVars *ptv);
40
static int PcapDirectoryGetModifiedTime(char const * file, struct timespec * out);
41
static TmEcode PcapDirectoryInsertFile(PcapFileDirectoryVars *pv,
42
                                       PendingFile *file_to_add);
43
static TmEcode PcapDirectoryPopulateBuffer(PcapFileDirectoryVars *ptv,
44
                                           struct timespec * older_than);
45
static TmEcode PcapDirectoryDispatchForTimeRange(PcapFileDirectoryVars *pv,
46
                                                 struct timespec *older_than);
47
48
void GetTime(struct timespec *tm)
49
0
{
50
0
    struct timeval now;
51
0
    if(gettimeofday(&now, NULL) == 0) {
52
0
        tm->tv_sec  = now.tv_sec;
53
0
        tm->tv_nsec = now.tv_usec * 1000L;
54
0
    }
55
0
}
56
57
void CopyTime(struct timespec *from, struct timespec *to)
58
0
{
59
0
    to->tv_sec = from->tv_sec;
60
0
    to->tv_nsec = from->tv_nsec;
61
0
}
62
63
int CompareTimes(struct timespec *left, struct timespec *right)
64
0
{
65
0
    if (left->tv_sec < right->tv_sec) {
66
0
        return -1;
67
0
    } else if (left->tv_sec > right->tv_sec) {
68
0
        return 1;
69
0
    } else {
70
0
        if (left->tv_nsec < right->tv_nsec) {
71
0
            return -1;
72
0
        } else if (left->tv_nsec > right->tv_nsec) {
73
0
            return 1;
74
0
        } else {
75
0
            return 0;
76
0
        }
77
0
    }
78
0
}
79
80
/**
81
 * Pcap Folder Utilities
82
 */
83
TmEcode PcapRunStatus(PcapFileDirectoryVars *ptv)
84
0
{
85
0
    if (RunModeUnixSocketIsActive()) {
86
0
        TmEcode done = UnixSocketPcapFile(TM_ECODE_OK, &ptv->shared->last_processed);
87
0
        if ( (suricata_ctl_flags & SURICATA_STOP) || done != TM_ECODE_OK) {
88
0
            SCReturnInt(TM_ECODE_DONE);
89
0
        }
90
0
    } else {
91
0
        if (suricata_ctl_flags & SURICATA_STOP) {
92
0
            SCReturnInt(TM_ECODE_DONE);
93
0
        }
94
0
    }
95
0
    SCReturnInt(TM_ECODE_OK);
96
0
}
97
98
0
void CleanupPendingFile(PendingFile *pending) {
99
0
    if (pending != NULL) {
100
0
        if (pending->filename != NULL) {
101
0
            SCFree(pending->filename);
102
0
        }
103
0
        SCFree(pending);
104
0
    }
105
0
}
106
107
void CleanupPcapFileDirectoryVars(PcapFileDirectoryVars *ptv)
108
0
{
109
0
    if (ptv != NULL) {
110
0
        if (ptv->current_file != NULL) {
111
0
            CleanupPcapFileFileVars(ptv->current_file);
112
0
            ptv->current_file = NULL;
113
0
        }
114
0
        if (ptv->directory != NULL) {
115
0
            closedir(ptv->directory);
116
0
            ptv->directory = NULL;
117
0
        }
118
0
        if (ptv->filename != NULL) {
119
0
            SCFree(ptv->filename);
120
0
        }
121
0
        ptv->shared = NULL;
122
0
        PendingFile *current_file = NULL;
123
0
        while (!TAILQ_EMPTY(&ptv->directory_content)) {
124
0
            current_file = TAILQ_FIRST(&ptv->directory_content);
125
0
            TAILQ_REMOVE(&ptv->directory_content, current_file, next);
126
0
            CleanupPendingFile(current_file);
127
0
        }
128
0
        SCFree(ptv);
129
0
    }
130
0
}
131
132
TmEcode PcapDirectoryFailure(PcapFileDirectoryVars *ptv)
133
0
{
134
0
    TmEcode status = TM_ECODE_FAILED;
135
136
0
    if (unlikely(ptv == NULL)) {
137
0
        SCLogError("Directory vars was null");
138
0
        SCReturnInt(TM_ECODE_FAILED);
139
0
    }
140
0
    if (unlikely(ptv->shared == NULL)) {
141
0
        SCLogError("Directory shared vars was null");
142
0
        SCReturnInt(TM_ECODE_FAILED);
143
0
    }
144
145
0
    if (RunModeUnixSocketIsActive()) {
146
0
        status = UnixSocketPcapFile(status, &ptv->shared->last_processed);
147
0
    }
148
149
0
    SCReturnInt(status);
150
0
}
151
152
TmEcode PcapDirectoryDone(PcapFileDirectoryVars *ptv)
153
0
{
154
0
    TmEcode status = TM_ECODE_DONE;
155
156
0
    if (unlikely(ptv == NULL)) {
157
0
        SCLogError("Directory vars was null");
158
0
        SCReturnInt(TM_ECODE_FAILED);
159
0
    }
160
0
    if (unlikely(ptv->shared == NULL)) {
161
0
        SCLogError("Directory shared vars was null");
162
0
        SCReturnInt(TM_ECODE_FAILED);
163
0
    }
164
165
0
    if (RunModeUnixSocketIsActive()) {
166
0
        status = UnixSocketPcapFile(status, &ptv->shared->last_processed);
167
0
    }
168
169
0
    SCReturnInt(status);
170
0
}
171
172
TmEcode PcapDetermineDirectoryOrFile(char *filename, DIR **directory)
173
24.7k
{
174
24.7k
    DIR *temp_dir = NULL;
175
24.7k
    TmEcode return_code = TM_ECODE_FAILED;
176
177
24.7k
    temp_dir = opendir(filename);
178
179
24.7k
    if (temp_dir == NULL) {//if null, our filename may just be a normal file
180
24.7k
        switch (errno) {
181
0
            case EACCES:
182
0
                SCLogError("%s: Permission denied", filename);
183
0
                break;
184
185
0
            case EBADF:
186
0
                SCLogError("%s: Not a valid file descriptor opened for reading", filename);
187
0
                break;
188
189
0
            case EMFILE:
190
0
                SCLogError("%s: Per process open file descriptor limit reached", filename);
191
0
                break;
192
193
0
            case ENFILE:
194
0
                SCLogError("%s: System wide open file descriptor limit reached", filename);
195
0
                break;
196
197
200
            case ENOENT:
198
200
                SCLogError("%s: Does not exist, or name is an empty string", filename);
199
200
                break;
200
0
            case ENOMEM:
201
0
                SCLogError("%s: Insufficient memory to complete the operation", filename);
202
0
                break;
203
204
24.5k
            case ENOTDIR: //no error checking the directory, just is a plain file
205
24.5k
                SCLogDebug("%s: plain file, not a directory", filename);
206
24.5k
                return_code = TM_ECODE_OK;
207
24.5k
                break;
208
209
0
            default:
210
0
                SCLogError("%s: %" PRId32, filename, errno);
211
24.7k
        }
212
24.7k
    } else {
213
        //no error, filename references a directory
214
0
        *directory = temp_dir;
215
0
        return_code = TM_ECODE_OK;
216
0
    }
217
218
24.7k
    return return_code;
219
24.7k
}
220
221
int PcapDirectoryGetModifiedTime(char const *file, struct timespec *out)
222
0
{
223
0
    SCStat buf;
224
0
    int ret;
225
226
0
    if (file == NULL)
227
0
        return -1;
228
229
0
    if ((ret = SCStatFn(file, &buf)) != 0)
230
0
        return ret;
231
232
#ifdef OS_DARWIN
233
    out->tv_sec = buf.st_mtimespec.tv_sec;
234
    out->tv_nsec = buf.st_mtimespec.tv_nsec;
235
#elif OS_WIN32
236
    out->tv_sec = buf.st_mtime;
237
#else
238
0
    out->tv_sec = buf.st_mtim.tv_sec;
239
0
    out->tv_nsec = buf.st_mtim.tv_nsec;
240
0
#endif
241
242
0
    return ret;
243
0
}
244
245
TmEcode PcapDirectoryInsertFile(PcapFileDirectoryVars *pv,
246
                                PendingFile *file_to_add
247
0
) {
248
0
    PendingFile *file_to_compare = NULL;
249
0
    PendingFile *next_file_to_compare = NULL;
250
251
0
    if (unlikely(pv == NULL)) {
252
0
        SCLogError("No directory vars passed");
253
0
        SCReturnInt(TM_ECODE_FAILED);
254
0
    }
255
256
0
    if (unlikely(file_to_add == NULL)) {
257
0
        SCLogError("File passed was null");
258
0
        SCReturnInt(TM_ECODE_FAILED);
259
0
    }
260
261
0
    if (unlikely(file_to_add->filename == NULL)) {
262
0
        SCLogError("File was passed with null filename");
263
0
        SCReturnInt(TM_ECODE_FAILED);
264
0
    }
265
266
0
    SCLogDebug("Inserting %s into directory buffer", file_to_add->filename);
267
268
0
    if (TAILQ_EMPTY(&pv->directory_content)) {
269
0
        TAILQ_INSERT_TAIL(&pv->directory_content, file_to_add, next);
270
0
    } else {
271
0
        file_to_compare = TAILQ_FIRST(&pv->directory_content);
272
0
        while(file_to_compare != NULL) {
273
0
            if (CompareTimes(&file_to_add->modified_time, &file_to_compare->modified_time) < 0) {
274
0
                TAILQ_INSERT_BEFORE(file_to_compare, file_to_add, next);
275
0
                file_to_compare = NULL;
276
0
            } else {
277
0
                next_file_to_compare = TAILQ_NEXT(file_to_compare, next);
278
0
                if (next_file_to_compare == NULL) {
279
0
                    TAILQ_INSERT_AFTER(&pv->directory_content, file_to_compare,
280
0
                                       file_to_add, next);
281
0
                }
282
0
                file_to_compare = next_file_to_compare;
283
0
            }
284
0
        }
285
0
    }
286
287
0
    SCReturnInt(TM_ECODE_OK);
288
0
}
289
290
TmEcode PcapDirectoryPopulateBuffer(PcapFileDirectoryVars *pv,
291
                                    struct timespec *older_than
292
0
) {
293
0
    if (unlikely(pv == NULL)) {
294
0
        SCLogError("No directory vars passed");
295
0
        SCReturnInt(TM_ECODE_FAILED);
296
0
    }
297
0
    if (unlikely(pv->filename == NULL)) {
298
0
        SCLogError("No directory filename was passed");
299
0
        SCReturnInt(TM_ECODE_FAILED);
300
0
    }
301
0
    struct dirent * dir = NULL;
302
0
    PendingFile *file_to_add = NULL;
303
304
0
    while ((dir = readdir(pv->directory)) != NULL) {
305
0
#ifndef OS_WIN32
306
0
        if (dir->d_type != DT_REG) {
307
0
            continue;
308
0
        }
309
0
#endif
310
0
        if (strcmp(dir->d_name, ".") == 0 ||
311
0
            strcmp(dir->d_name, "..") == 0) {
312
0
            continue;
313
0
        }
314
315
0
        char pathbuff[PATH_MAX] = {0};
316
317
0
        int written = 0;
318
319
0
        written = snprintf(pathbuff, PATH_MAX, "%s/%s", pv->filename, dir->d_name);
320
321
0
        if (written <= 0 || written >= PATH_MAX) {
322
0
            SCLogError("Could not write path");
323
324
0
            SCReturnInt(TM_ECODE_FAILED);
325
0
        } else {
326
0
            struct timespec temp_time;
327
0
            memset(&temp_time, 0, sizeof(struct timespec));
328
329
0
            if (PcapDirectoryGetModifiedTime(pathbuff, &temp_time) == 0) {
330
0
                SCLogDebug("%" PRIuMAX " < %" PRIuMAX "(%s) < %" PRIuMAX ")",
331
0
                           (uintmax_t)SCTimespecAsEpochMillis(&pv->shared->last_processed),
332
0
                           (uintmax_t)SCTimespecAsEpochMillis(&temp_time),
333
0
                           pathbuff,
334
0
                           (uintmax_t)SCTimespecAsEpochMillis(older_than));
335
336
                // Skip files outside of our time range
337
0
                if (CompareTimes(&temp_time, &pv->shared->last_processed) <= 0) {
338
0
                    SCLogDebug("Skipping old file %s", pathbuff);
339
0
                    continue;
340
0
                }
341
0
                else if (CompareTimes(&temp_time, older_than) >= 0) {
342
0
                    SCLogDebug("Skipping new file %s", pathbuff);
343
0
                    continue;
344
0
                }
345
0
            } else {
346
0
                SCLogDebug("Unable to get modified time on %s, skipping", pathbuff);
347
0
                continue;
348
0
            }
349
350
0
            file_to_add = SCCalloc(1, sizeof(PendingFile));
351
0
            if (unlikely(file_to_add == NULL)) {
352
0
                SCLogError("Failed to allocate pending file");
353
354
0
                SCReturnInt(TM_ECODE_FAILED);
355
0
            }
356
357
0
            file_to_add->filename = SCStrdup(pathbuff);
358
0
            if (unlikely(file_to_add->filename == NULL)) {
359
0
                SCLogError("Failed to copy filename");
360
0
                CleanupPendingFile(file_to_add);
361
362
0
                SCReturnInt(TM_ECODE_FAILED);
363
0
            }
364
365
0
            memset(&file_to_add->modified_time, 0, sizeof(struct timespec));
366
0
            CopyTime(&temp_time, &file_to_add->modified_time);
367
368
0
            SCLogInfo("Found \"%s\" at %" PRIuMAX, file_to_add->filename,
369
0
                       (uintmax_t)SCTimespecAsEpochMillis(&file_to_add->modified_time));
370
371
0
            if (PcapDirectoryInsertFile(pv, file_to_add) == TM_ECODE_FAILED) {
372
0
                SCLogError("Failed to add file");
373
0
                CleanupPendingFile(file_to_add);
374
375
0
                SCReturnInt(TM_ECODE_FAILED);
376
0
            }
377
0
        }
378
0
    }
379
380
0
    SCReturnInt(TM_ECODE_OK);
381
0
}
382
383
384
TmEcode PcapDirectoryDispatchForTimeRange(PcapFileDirectoryVars *pv,
385
                                          struct timespec *older_than)
386
0
{
387
0
    if (PcapDirectoryPopulateBuffer(pv, older_than) == TM_ECODE_FAILED) {
388
0
        SCLogError("Failed to populate directory buffer");
389
0
        SCReturnInt(TM_ECODE_FAILED);
390
0
    }
391
392
0
    TmEcode status = TM_ECODE_OK;
393
394
0
    if (TAILQ_EMPTY(&pv->directory_content)) {
395
0
        SCLogDebug("Directory %s has no files to process", pv->filename);
396
0
        GetTime(older_than);
397
0
        older_than->tv_sec = older_than->tv_sec - pv->delay;
398
0
        rewinddir(pv->directory);
399
0
        status = TM_ECODE_OK;
400
0
    } else {
401
0
        PendingFile *current_file = NULL;
402
403
0
        struct timespec last_time_seen;
404
0
        memset(&last_time_seen, 0, sizeof(struct timespec));
405
406
0
        while (status == TM_ECODE_OK && !TAILQ_EMPTY(&pv->directory_content)) {
407
0
            current_file = TAILQ_FIRST(&pv->directory_content);
408
0
            TAILQ_REMOVE(&pv->directory_content, current_file, next);
409
410
0
            if (unlikely(current_file == NULL)) {
411
0
                SCLogWarning("Current file was null");
412
0
            } else if (unlikely(current_file->filename == NULL)) {
413
0
                SCLogWarning("Current file filename was null");
414
0
            } else {
415
0
                SCLogDebug("Processing file %s", current_file->filename);
416
417
0
                PcapFileFileVars *pftv = SCMalloc(sizeof(PcapFileFileVars));
418
0
                if (unlikely(pftv == NULL)) {
419
0
                    SCLogError("Failed to allocate PcapFileFileVars");
420
0
                    SCReturnInt(TM_ECODE_FAILED);
421
0
                }
422
0
                memset(pftv, 0, sizeof(PcapFileFileVars));
423
424
0
                pftv->filename = SCStrdup(current_file->filename);
425
0
                if (unlikely(pftv->filename == NULL)) {
426
0
                    SCLogError("Failed to allocate filename");
427
0
                    CleanupPcapFileFileVars(pftv);
428
0
                    SCReturnInt(TM_ECODE_FAILED);
429
0
                }
430
0
                pftv->shared = pv->shared;
431
432
0
                if (InitPcapFile(pftv) == TM_ECODE_FAILED) {
433
0
                    SCLogWarning("Failed to init pcap file %s, skipping", current_file->filename);
434
0
                    CleanupPendingFile(current_file);
435
0
                    CleanupPcapFileFileVars(pftv);
436
0
                    status = TM_ECODE_OK;
437
0
                } else {
438
0
                    pv->current_file = pftv;
439
440
0
                    status = PcapFileDispatch(pftv);
441
442
0
                    CleanupPcapFileFileVars(pftv);
443
444
0
                    if (status == TM_ECODE_FAILED) {
445
0
                        CleanupPendingFile(current_file);
446
0
                        SCReturnInt(status);
447
0
                    }
448
449
0
                    SCLogInfo("Processed file %s, processed up to %" PRIuMAX,
450
0
                               current_file->filename,
451
0
                               (uintmax_t)SCTimespecAsEpochMillis(&current_file->modified_time));
452
453
0
                    if(CompareTimes(&current_file->modified_time, &last_time_seen) > 0) {
454
0
                        CopyTime(&current_file->modified_time, &last_time_seen);
455
0
                    }
456
457
0
                    CleanupPendingFile(current_file);
458
0
                    pv->current_file = NULL;
459
460
0
                    status = PcapRunStatus(pv);
461
0
                }
462
0
            }
463
0
        }
464
465
0
        if(CompareTimes(&last_time_seen, &pv->shared->last_processed) > 0) {
466
0
            SCLogInfo("Updating processed to %" PRIuMAX,
467
0
                      (uintmax_t)SCTimespecAsEpochMillis(&last_time_seen));
468
0
            CopyTime(&last_time_seen, &pv->shared->last_processed);
469
0
            status = PcapRunStatus(pv);
470
0
        }
471
0
    }
472
0
    GetTime(older_than);
473
0
    older_than->tv_sec = older_than->tv_sec - pv->delay;
474
475
0
    SCReturnInt(status);
476
0
}
477
478
TmEcode PcapDirectoryDispatch(PcapFileDirectoryVars *ptv)
479
0
{
480
0
    SCEnter();
481
482
0
    DIR *directory_check = NULL;
483
484
0
    struct timespec older_than;
485
0
    memset(&older_than, 0, sizeof(struct timespec));
486
0
    older_than.tv_sec = LONG_MAX;
487
0
    uint32_t poll_seconds = (uint32_t)localtime(&ptv->poll_interval)->tm_sec;
488
489
0
    if (ptv->should_loop) {
490
0
        GetTime(&older_than);
491
0
        older_than.tv_sec = older_than.tv_sec - ptv->delay;
492
0
    }
493
0
    TmEcode status = TM_ECODE_OK;
494
495
0
    while (status == TM_ECODE_OK) {
496
        //loop while directory is ok
497
0
        SCLogInfo("Processing pcaps directory %s, files must be newer than %" PRIuMAX " and older than %" PRIuMAX,
498
0
                  ptv->filename, (uintmax_t)SCTimespecAsEpochMillis(&ptv->shared->last_processed),
499
0
                  (uintmax_t)SCTimespecAsEpochMillis(&older_than));
500
0
        status = PcapDirectoryDispatchForTimeRange(ptv, &older_than);
501
0
        if (ptv->should_loop && status == TM_ECODE_OK) {
502
0
            sleep(poll_seconds);
503
            //update our status based on suricata control flags or unix command socket
504
0
            status = PcapRunStatus(ptv);
505
0
            if (status == TM_ECODE_OK) {
506
0
                SCLogDebug("Checking if directory %s still exists", ptv->filename);
507
                //check directory
508
0
                if (PcapDetermineDirectoryOrFile(ptv->filename,
509
0
                                                 &directory_check) == TM_ECODE_FAILED) {
510
0
                    SCLogInfo("Directory %s no longer exists, stopping",
511
0
                              ptv->filename);
512
0
                    status = TM_ECODE_DONE;
513
0
                } else if(directory_check != NULL) {
514
0
                    closedir(directory_check);
515
0
                    directory_check = NULL;
516
0
                }
517
0
            }
518
0
        } else if (status == TM_ECODE_OK) { //not looping, mark done
519
0
            SCLogDebug("Not looping, stopping directory mode");
520
0
            status = TM_ECODE_DONE;
521
0
        }
522
0
    }
523
524
0
    StatsSyncCountersIfSignalled(ptv->shared->tv);
525
526
0
    if (status == TM_ECODE_FAILED) {
527
0
        SCLogError("Directory %s run mode failed", ptv->filename);
528
0
        status = PcapDirectoryFailure(ptv);
529
0
    } else {
530
0
        SCLogInfo("Directory run mode complete");
531
0
        status = PcapDirectoryDone(ptv);
532
0
    }
533
534
0
    SCReturnInt(status);
535
0
}
536
537
/* eof */