Coverage Report

Created: 2025-11-09 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Pcap++/src/PcapFileDevice.cpp
Line
Count
Source
1
42.9k
#define LOG_MODULE PcapLogModuleFileDevice
2
3
#include <cerrno>
4
#include <stdexcept>
5
#include "PcapFileDevice.h"
6
#include "light_pcapng_ext.h"
7
#include "Logger.h"
8
#include "TimespecTimeval.h"
9
#include "pcap.h"
10
#include <fstream>
11
#include "EndianPortable.h"
12
13
namespace pcpp
14
{
15
  namespace
16
  {
17
    /// @brief Converts a light_pcapng_t* to an opaque LightPcapNgHandle*.
18
    /// @param pcapngHandle The light_pcapng_t* to convert.
19
    /// @return An pointer to the opaque handle.
20
    internal::LightPcapNgHandle* toLightPcapNgHandle(light_pcapng_t* pcapngHandle)
21
4.77k
    {
22
4.77k
      return reinterpret_cast<internal::LightPcapNgHandle*>(pcapngHandle);
23
4.77k
    }
24
25
    /// @brief Converts an opaque LightPcapNgHandle* to a light_pcapng_t*.
26
    /// @param pcapngHandle The LightPcapNgHandle* to convert.
27
    /// @return A pointer to the light_pcapng_t.
28
    light_pcapng_t* toLightPcapNgT(internal::LightPcapNgHandle* pcapngHandle)
29
35.0k
    {
30
35.0k
      return reinterpret_cast<light_pcapng_t*>(pcapngHandle);
31
35.0k
    }
32
  }  // namespace
33
34
  template <typename T, size_t N> constexpr size_t ARRAY_SIZE(T (&)[N])
35
213
  {
36
213
    return N;
37
213
  }
38
39
  struct pcap_file_header
40
  {
41
    uint32_t magic;
42
    uint16_t version_major;
43
    uint16_t version_minor;
44
    int32_t thiszone;
45
    uint32_t sigfigs;
46
    uint32_t snaplen;
47
    uint32_t linktype;
48
  };
49
50
  struct packet_header
51
  {
52
    uint32_t tv_sec;
53
    uint32_t tv_usec;
54
    uint32_t caplen;
55
    uint32_t len;
56
  };
57
58
  static bool checkNanoSupport()
59
0
  {
60
0
#if defined(PCAP_TSTAMP_PRECISION_NANO)
61
0
    return true;
62
#else
63
    PCPP_LOG_DEBUG(
64
        "PcapPlusPlus was compiled without nano precision support which requires libpcap > 1.5.1. Please "
65
        "recompile PcapPlusPlus with nano precision support to use this feature. Using default microsecond precision");
66
    return false;
67
#endif
68
0
  }
69
70
  // ~~~~~~~~~~~~~~~~~~~
71
  // IFileDevice members
72
  // ~~~~~~~~~~~~~~~~~~~
73
74
10.5k
  IFileDevice::IFileDevice(const std::string& fileName) : IPcapDevice()
75
10.5k
  {
76
10.5k
    m_FileName = fileName;
77
10.5k
  }
78
79
  IFileDevice::~IFileDevice()
80
10.5k
  {
81
10.5k
    IFileDevice::close();
82
10.5k
  }
83
84
  std::string IFileDevice::getFileName() const
85
0
  {
86
0
    return m_FileName;
87
0
  }
88
89
  void IFileDevice::close()
90
11.5k
  {
91
11.5k
    if (m_PcapDescriptor != nullptr)
92
3.85k
    {
93
3.85k
      m_PcapDescriptor = nullptr;
94
3.85k
      PCPP_LOG_DEBUG("Successfully closed file reader device for filename '" << m_FileName << "'");
95
3.85k
    }
96
97
11.5k
    m_DeviceOpened = false;
98
11.5k
  }
99
100
  void IFileDevice::getStatistics(PcapStats& stats) const
101
3.06k
  {
102
3.06k
    PCPP_LOG_DEBUG("Statistics requested for file device for filename '" << m_FileName << "'");
103
3.06k
    stats.packetsRecv = m_NumOfPacketsProcessed;
104
3.06k
    stats.packetsDrop = m_NumOfPacketsDropped;
105
3.06k
    stats.packetsDropByInterface = 0;
106
3.06k
  }
107
108
  void IFileDevice::resetStatisticCounters()
109
9.60k
  {
110
9.60k
    m_NumOfPacketsProcessed = 0;
111
9.60k
    m_NumOfPacketsDropped = 0;
112
9.60k
  }
113
114
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
115
  // IFileReaderDevice members
116
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
117
118
5.74k
  IFileReaderDevice::IFileReaderDevice(const std::string& fileName) : IFileDevice(fileName)
119
5.74k
  {}
120
121
  IFileReaderDevice* IFileReaderDevice::getReader(const std::string& fileName)
122
5.74k
  {
123
5.74k
    const auto extensionPos = fileName.find_last_of('.');
124
5.74k
    const auto fileExtension = extensionPos != std::string::npos ? fileName.substr(extensionPos) : "";
125
126
5.74k
    if (fileExtension == ".pcapng" || fileExtension == ".zstd" || fileExtension == ".zst")
127
1.02k
      return new PcapNgFileReaderDevice(fileName);
128
4.72k
    else if (fileExtension == ".snoop")
129
216
      return new SnoopFileReaderDevice(fileName);
130
131
4.51k
    return new PcapFileReaderDevice(fileName);
132
5.74k
  }
133
134
  uint64_t IFileReaderDevice::getFileSize() const
135
0
  {
136
0
    std::ifstream fileStream(m_FileName.c_str(), std::ifstream::ate | std::ifstream::binary);
137
0
    return fileStream.tellg();
138
0
  }
139
140
  int IFileReaderDevice::getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead)
141
4.94k
  {
142
4.94k
    int numOfPacketsRead = 0;
143
144
7.85k
    for (; numOfPacketsToRead < 0 || numOfPacketsRead < numOfPacketsToRead; numOfPacketsRead++)
145
4.94k
    {
146
4.94k
      RawPacket* newPacket = new RawPacket();
147
4.94k
      bool packetRead = getNextPacket(*newPacket);
148
4.94k
      if (packetRead)
149
2.91k
      {
150
2.91k
        packetVec.pushBack(newPacket);
151
2.91k
      }
152
2.02k
      else
153
2.02k
      {
154
2.02k
        delete newPacket;
155
2.02k
        break;
156
2.02k
      }
157
4.94k
    }
158
159
4.94k
    return numOfPacketsRead;
160
4.94k
  }
161
162
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
163
  // IFileWriterDevice members
164
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
165
166
4.77k
  IFileWriterDevice::IFileWriterDevice(const std::string& fileName) : IFileDevice(fileName)
167
4.77k
  {}
168
169
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
170
  // PcapFileReaderDevice members
171
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
172
173
  bool PcapFileReaderDevice::open()
174
4.51k
  {
175
4.51k
    resetStatisticCounters();
176
177
4.51k
    if (m_PcapDescriptor != nullptr)
178
0
    {
179
0
      PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do");
180
0
      return true;
181
0
    }
182
183
4.51k
    char errbuf[PCAP_ERRBUF_SIZE];
184
4.51k
#if defined(PCAP_TSTAMP_PRECISION_NANO)
185
4.51k
    auto pcapDescriptor = internal::PcapHandle(
186
4.51k
        pcap_open_offline_with_tstamp_precision(m_FileName.c_str(), PCAP_TSTAMP_PRECISION_NANO, errbuf));
187
#else
188
    auto pcapDescriptor = internal::PcapHandle(pcap_open_offline(m_FileName.c_str(), errbuf));
189
#endif
190
4.51k
    if (pcapDescriptor == nullptr)
191
706
    {
192
706
      PCPP_LOG_ERROR("Cannot open file reader device for filename '" << m_FileName << "': " << errbuf);
193
706
      m_DeviceOpened = false;
194
706
      return false;
195
706
    }
196
197
3.80k
    int linkLayer = pcap_datalink(pcapDescriptor.get());
198
3.80k
    if (!RawPacket::isLinkTypeValid(linkLayer))
199
53
    {
200
53
      PCPP_LOG_ERROR("Invalid link layer (" << linkLayer << ") for reader device filename '" << m_FileName
201
53
                                            << "'");
202
53
      m_DeviceOpened = false;
203
53
      return false;
204
53
    }
205
206
3.75k
    m_PcapLinkLayerType = static_cast<LinkLayerType>(linkLayer);
207
208
3.75k
#if defined(PCAP_TSTAMP_PRECISION_NANO)
209
3.75k
    m_Precision = static_cast<FileTimestampPrecision>(pcap_get_tstamp_precision(pcapDescriptor.get()));
210
3.75k
    std::string precisionStr =
211
3.75k
        (m_Precision == FileTimestampPrecision::Nanoseconds) ? "nanoseconds" : "microseconds";
212
#else
213
    m_Precision = FileTimestampPrecision::Microseconds;
214
    std::string precisionStr = "microseconds";
215
#endif
216
3.75k
    PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "' with precision "
217
3.75k
                                                                           << precisionStr);
218
3.75k
    m_PcapDescriptor = std::move(pcapDescriptor);
219
3.75k
    m_DeviceOpened = true;
220
3.75k
    return true;
221
3.80k
  }
222
223
  bool PcapFileReaderDevice::isNanoSecondPrecisionSupported()
224
0
  {
225
0
    return checkNanoSupport();
226
0
  }
227
228
  bool PcapFileReaderDevice::getNextPacket(RawPacket& rawPacket)
229
27.8k
  {
230
27.8k
    if (m_PcapDescriptor == nullptr)
231
0
    {
232
0
      PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened");
233
0
      return false;
234
0
    }
235
27.8k
    pcap_pkthdr pkthdr;
236
27.8k
    const uint8_t* pPacketData = pcap_next(m_PcapDescriptor.get(), &pkthdr);
237
27.8k
    if (pPacketData == nullptr)
238
3.75k
    {
239
3.75k
      PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file");
240
3.75k
      return false;
241
3.75k
    }
242
243
24.1k
    uint8_t* pMyPacketData = new uint8_t[pkthdr.caplen];
244
24.1k
    memcpy(pMyPacketData, pPacketData, pkthdr.caplen);
245
24.1k
#if defined(PCAP_TSTAMP_PRECISION_NANO)
246
    // because we opened with nano second precision 'tv_usec' is actually nanos
247
24.1k
    timespec ts = { pkthdr.ts.tv_sec, static_cast<long>(pkthdr.ts.tv_usec) };
248
#else
249
    struct timeval ts = pkthdr.ts;
250
#endif
251
24.1k
    if (!rawPacket.setRawData(pMyPacketData, pkthdr.caplen, ts, static_cast<LinkLayerType>(m_PcapLinkLayerType),
252
24.1k
                              pkthdr.len))
253
0
    {
254
0
      PCPP_LOG_ERROR("Couldn't set data to raw packet");
255
0
      return false;
256
0
    }
257
24.1k
    reportPacketProcessed();
258
24.1k
    return true;
259
24.1k
  }
260
261
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
262
  // PcapFileWriterDevice members
263
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
264
265
  PcapFileWriterDevice::PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType,
266
                                             bool nanosecondsPrecision)
267
1.02k
      : IFileWriterDevice(fileName)
268
1.02k
  {
269
1.02k
    m_PcapDumpHandler = nullptr;
270
1.02k
    m_PcapLinkLayerType = linkLayerType;
271
1.02k
    m_AppendMode = false;
272
1.02k
#if defined(PCAP_TSTAMP_PRECISION_NANO)
273
1.02k
    m_Precision = nanosecondsPrecision ? FileTimestampPrecision::Nanoseconds : FileTimestampPrecision::Microseconds;
274
#else
275
    if (nanosecondsPrecision)
276
    {
277
      throw std::runtime_error(
278
          "PcapPlusPlus was compiled without nano precision support which requires libpcap > 1.5.1. "
279
          "Please recompile PcapPlusPlus with nano precision support to use this feature.");
280
    }
281
    m_Precision = FileTimestampPrecision::Microseconds;
282
#endif
283
1.02k
    m_File = nullptr;
284
1.02k
  }
285
286
  void PcapFileWriterDevice::closeFile()
287
0
  {
288
0
    if (m_AppendMode && m_File != nullptr)
289
0
    {
290
0
      fclose(m_File);
291
0
      m_File = nullptr;
292
0
    }
293
0
  }
294
295
  bool PcapFileWriterDevice::writePacket(RawPacket const& packet)
296
5.16k
  {
297
5.16k
    if ((!m_AppendMode && m_PcapDescriptor == nullptr) || (m_PcapDumpHandler == nullptr))
298
0
    {
299
0
      PCPP_LOG_ERROR("Device not opened");
300
0
      reportPacketDropped();
301
0
      return false;
302
0
    }
303
304
5.16k
    if (packet.getLinkLayerType() != m_PcapLinkLayerType)
305
4.01k
    {
306
4.01k
      PCPP_LOG_ERROR("Cannot write a packet with a different link layer type");
307
4.01k
      reportPacketDropped();
308
4.01k
      return false;
309
4.01k
    }
310
311
1.14k
    pcap_pkthdr pktHdr;
312
1.14k
    pktHdr.caplen = packet.getRawDataLen();
313
1.14k
    pktHdr.len = packet.getFrameLength();
314
1.14k
    timespec packet_timestamp = packet.getPacketTimeStamp();
315
1.14k
#if defined(PCAP_TSTAMP_PRECISION_NANO)
316
1.14k
    if (m_Precision != FileTimestampPrecision::Nanoseconds)
317
1.14k
    {
318
1.14k
      pktHdr.ts = internal::toTimeval(packet_timestamp);
319
1.14k
    }
320
0
    else
321
0
    {
322
0
      pktHdr.ts.tv_sec = packet_timestamp.tv_sec;
323
0
      pktHdr.ts.tv_usec = packet_timestamp.tv_nsec;
324
0
    }
325
#else
326
    pktHdr.ts = internal::toTimeval(packet_timestamp);
327
#endif
328
1.14k
    if (!m_AppendMode)
329
85
      pcap_dump(reinterpret_cast<uint8_t*>(m_PcapDumpHandler), &pktHdr, packet.getRawData());
330
1.05k
    else
331
1.05k
    {
332
      // Below are actually the lines run by pcap_dump. The reason I had to put them instead pcap_dump is that on
333
      // Windows using WinPcap/Npcap you can't pass pointers between libraries compiled with different compilers.
334
      // In this case - PcapPlusPlus and WinPcap/Npcap weren't compiled with the same compiler so it's impossible
335
      // to fopen a file in PcapPlusPlus, pass the pointer to WinPcap/Npcap and use the FILE* pointer there. Doing
336
      // this throws an exception. So the only option when implementing append to pcap is to write all relevant
337
      // WinPcap/Npcap code that handles opening/closing/writing to pcap files inside PcapPlusPlus code
338
339
      // the reason to create this packet_header struct is timeval has different sizes in 32-bit and 64-bit
340
      // systems, but pcap format uses the 32-bit timeval version, so we need to align timeval to that
341
1.05k
      packet_header pktHdrTemp;
342
1.05k
      pktHdrTemp.tv_sec = pktHdr.ts.tv_sec;
343
1.05k
      pktHdrTemp.tv_usec = pktHdr.ts.tv_usec;
344
1.05k
      pktHdrTemp.caplen = pktHdr.caplen;
345
1.05k
      pktHdrTemp.len = pktHdr.len;
346
1.05k
      fwrite(&pktHdrTemp, sizeof(pktHdrTemp), 1, m_File);
347
1.05k
      fwrite(packet.getRawData(), pktHdrTemp.caplen, 1, m_File);
348
1.05k
    }
349
1.14k
    PCPP_LOG_DEBUG("Packet written successfully to '" << m_FileName << "'");
350
1.14k
    reportPacketProcessed();
351
1.14k
    return true;
352
5.16k
  }
353
354
  bool PcapFileWriterDevice::writePackets(const RawPacketVector& packets)
355
0
  {
356
0
    for (auto packet : packets)
357
0
    {
358
0
      if (!writePacket(*packet))
359
0
        return false;
360
0
    }
361
362
0
    return true;
363
0
  }
364
365
  bool PcapFileWriterDevice::isNanoSecondPrecisionSupported()
366
0
  {
367
0
    return checkNanoSupport();
368
0
  }
369
370
  bool PcapFileWriterDevice::open()
371
0
  {
372
0
    return open(false);
373
0
  }
374
375
  bool PcapFileWriterDevice::open(bool appendMode)
376
1.02k
  {
377
1.02k
    if (isOpened())
378
0
    {
379
      // TODO: Ambiguity in API
380
      //   If appendMode is required but the file is already opened in write mode.
381
0
      PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do");
382
0
      return true;
383
0
    }
384
385
1.02k
    if (appendMode)
386
919
    {
387
919
      return openAppend();
388
919
    }
389
103
    else
390
103
    {
391
103
      return openWrite();
392
103
    }
393
1.02k
  }
394
395
  bool PcapFileWriterDevice::openWrite()
396
103
  {
397
103
    m_AppendMode = false;
398
399
103
    switch (m_PcapLinkLayerType)
400
103
    {
401
0
    case LINKTYPE_RAW:
402
0
    case LINKTYPE_DLT_RAW2:
403
0
      PCPP_LOG_ERROR(
404
0
          "The only Raw IP link type supported in libpcap/WinPcap/Npcap is LINKTYPE_DLT_RAW1, please use that instead");
405
0
      return false;
406
103
    default:
407
103
      break;
408
103
    }
409
410
103
    resetStatisticCounters();
411
412
103
#if defined(PCAP_TSTAMP_PRECISION_NANO)
413
103
    auto pcapDescriptor = internal::PcapHandle(pcap_open_dead_with_tstamp_precision(
414
103
        m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE, static_cast<int>(m_Precision)));
415
#else
416
    auto pcapDescriptor = internal::PcapHandle(pcap_open_dead(m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE));
417
#endif
418
103
    if (pcapDescriptor == nullptr)
419
0
    {
420
0
      PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName
421
0
                                                                   << "': pcap_open_dead returned nullptr");
422
0
      m_DeviceOpened = false;
423
0
      return false;
424
0
    }
425
426
103
    m_PcapDumpHandler = pcap_dump_open(pcapDescriptor.get(), m_FileName.c_str());
427
103
    if (m_PcapDumpHandler == nullptr)
428
0
    {
429
0
      PCPP_LOG_ERROR("Error opening file writer device for file '"
430
0
                     << m_FileName << "': pcap_dump_open returned nullptr with error: '"
431
0
                     << pcapDescriptor.getLastError() << "'");
432
0
      m_DeviceOpened = false;
433
0
      return false;
434
0
    }
435
436
103
    m_PcapDescriptor = std::move(pcapDescriptor);
437
103
    m_DeviceOpened = true;
438
103
    PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully");
439
103
    return true;
440
103
  }
441
442
  bool PcapFileWriterDevice::openAppend()
443
919
  {
444
919
    m_AppendMode = true;
445
446
919
#if !defined(_WIN32)
447
919
    m_File = fopen(m_FileName.c_str(), "r+");
448
#else
449
    m_File = fopen(m_FileName.c_str(), "rb+");
450
#endif
451
452
919
    if (m_File == nullptr)
453
0
    {
454
0
      PCPP_LOG_ERROR("Cannot open '" << m_FileName << "' for reading and writing");
455
0
      return false;
456
0
    }
457
458
919
    pcap_file_header pcapFileHeader;
459
919
    int amountRead = fread(&pcapFileHeader, 1, sizeof(pcapFileHeader), m_File);
460
919
    if (amountRead != sizeof(pcap_file_header))
461
0
    {
462
0
      if (ferror(m_File))
463
0
        PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', error was: " << errno);
464
0
      else
465
0
        PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', unknown error");
466
467
0
      closeFile();
468
0
      return false;
469
0
    }
470
471
919
    LinkLayerType linkLayerType = static_cast<LinkLayerType>(pcapFileHeader.linktype);
472
919
    if (linkLayerType != m_PcapLinkLayerType)
473
0
    {
474
0
      PCPP_LOG_ERROR(
475
0
          "Pcap file has a different link layer type than the one chosen in PcapFileWriterDevice c'tor, "
476
0
          << linkLayerType << ", " << m_PcapLinkLayerType);
477
0
      closeFile();
478
0
      return false;
479
0
    }
480
481
919
    if (fseek(m_File, 0, SEEK_END) == -1)
482
0
    {
483
0
      PCPP_LOG_ERROR("Cannot read pcap file '" << m_FileName << "' to it's end, error was: " << errno);
484
0
      closeFile();
485
0
      return false;
486
0
    }
487
488
919
    m_PcapDumpHandler = reinterpret_cast<pcap_dumper_t*>(m_File);
489
490
919
    m_DeviceOpened = true;
491
919
    PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully in append mode");
492
919
    return true;
493
919
  }
494
495
  void PcapFileWriterDevice::flush()
496
1.02k
  {
497
1.02k
    if (!m_DeviceOpened)
498
0
      return;
499
500
1.02k
    if (!m_AppendMode && pcap_dump_flush(m_PcapDumpHandler) == -1)
501
0
    {
502
0
      PCPP_LOG_ERROR("Error while flushing the packets to file");
503
0
    }
504
    // in append mode it's impossible to use pcap_dump_flush, see comment above pcap_dump
505
1.02k
    else if (m_AppendMode && fflush(m_File) == EOF)
506
0
    {
507
0
      PCPP_LOG_ERROR("Error while flushing the packets to file");
508
0
    }
509
1.02k
  }
510
511
  void PcapFileWriterDevice::close()
512
1.39k
  {
513
1.39k
    if (!m_DeviceOpened)
514
372
      return;
515
516
1.02k
    flush();
517
518
1.02k
    IFileDevice::close();
519
520
1.02k
    if (!m_AppendMode && m_PcapDumpHandler != nullptr)
521
103
    {
522
103
      pcap_dump_close(m_PcapDumpHandler);
523
103
    }
524
919
    else if (m_AppendMode && m_File != nullptr)
525
919
    {
526
      // in append mode it's impossible to use pcap_dump_close, see comment above pcap_dump
527
919
      fclose(m_File);
528
919
    }
529
530
1.02k
    m_PcapDumpHandler = nullptr;
531
1.02k
    m_File = nullptr;
532
1.02k
    PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'");
533
1.02k
  }
534
535
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
536
  // PcapNgFileReaderDevice members
537
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
538
539
1.02k
  PcapNgFileReaderDevice::PcapNgFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName)
540
1.02k
  {
541
1.02k
    m_LightPcapNg = nullptr;
542
1.02k
  }
543
544
  bool PcapNgFileReaderDevice::open()
545
1.02k
  {
546
1.02k
    resetStatisticCounters();
547
548
1.02k
    if (m_LightPcapNg != nullptr)
549
0
    {
550
0
      PCPP_LOG_DEBUG("pcapng descriptor already opened. Nothing to do");
551
0
      return true;
552
0
    }
553
554
1.02k
    m_LightPcapNg = toLightPcapNgHandle(light_pcapng_open_read(m_FileName.c_str(), LIGHT_FALSE));
555
1.02k
    if (m_LightPcapNg == nullptr)
556
0
    {
557
0
      PCPP_LOG_ERROR("Cannot open pcapng reader device for filename '" << m_FileName << "'");
558
0
      m_DeviceOpened = false;
559
0
      return false;
560
0
    }
561
562
1.02k
    PCPP_LOG_DEBUG("Successfully opened pcapng reader device for filename '" << m_FileName << "'");
563
1.02k
    m_DeviceOpened = true;
564
1.02k
    return true;
565
1.02k
  }
566
567
  bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket, std::string& packetComment)
568
6.18k
  {
569
6.18k
    if (m_LightPcapNg == nullptr)
570
0
    {
571
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
572
0
      return false;
573
0
    }
574
575
6.18k
    light_packet_header pktHeader;
576
6.18k
    const uint8_t* pktData = nullptr;
577
578
6.18k
    if (!light_get_next_packet(toLightPcapNgT(m_LightPcapNg), &pktHeader, &pktData))
579
1.02k
    {
580
1.02k
      PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file");
581
1.02k
      return false;
582
1.02k
    }
583
584
5.16k
    while (!m_BpfWrapper.matches(pktData, pktHeader.captured_length, pktHeader.timestamp, pktHeader.data_link))
585
0
    {
586
0
      if (!light_get_next_packet(toLightPcapNgT(m_LightPcapNg), &pktHeader, &pktData))
587
0
      {
588
0
        PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file");
589
0
        return false;
590
0
      }
591
0
    }
592
593
5.16k
    uint8_t* myPacketData = new uint8_t[pktHeader.captured_length];
594
5.16k
    memcpy(myPacketData, pktData, pktHeader.captured_length);
595
5.16k
    const LinkLayerType linkType = static_cast<LinkLayerType>(pktHeader.data_link);
596
5.16k
    if (linkType == LinkLayerType::LINKTYPE_INVALID)
597
2.46k
    {
598
2.46k
      PCPP_LOG_ERROR("Link layer type of raw packet could not be determined");
599
2.46k
    }
600
601
5.16k
    if (!rawPacket.setRawData(myPacketData, pktHeader.captured_length, pktHeader.timestamp, linkType,
602
5.16k
                              pktHeader.original_length))
603
0
    {
604
0
      PCPP_LOG_ERROR("Couldn't set data to raw packet");
605
0
      return false;
606
0
    }
607
608
5.16k
    if (pktHeader.comment != nullptr && pktHeader.comment_length > 0)
609
682
    {
610
682
      packetComment = std::string(pktHeader.comment, pktHeader.comment_length);
611
682
    }
612
4.47k
    else
613
4.47k
    {
614
4.47k
      packetComment.clear();
615
4.47k
    }
616
617
5.16k
    reportPacketProcessed();
618
5.16k
    return true;
619
5.16k
  }
620
621
  bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket)
622
6.18k
  {
623
6.18k
    std::string temp;
624
6.18k
    return getNextPacket(rawPacket, temp);
625
6.18k
  }
626
627
  bool PcapNgFileReaderDevice::setFilter(std::string filterAsString)
628
0
  {
629
0
    return m_BpfWrapper.setFilter(filterAsString);
630
0
  }
631
632
  void PcapNgFileReaderDevice::close()
633
1.02k
  {
634
1.02k
    if (m_LightPcapNg == nullptr)
635
0
      return;
636
637
1.02k
    light_pcapng_close(toLightPcapNgT(m_LightPcapNg));
638
1.02k
    m_LightPcapNg = nullptr;
639
640
1.02k
    m_DeviceOpened = false;
641
1.02k
    PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'");
642
1.02k
  }
643
644
  std::string PcapNgFileReaderDevice::getOS() const
645
0
  {
646
0
    if (m_LightPcapNg == nullptr)
647
0
    {
648
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
649
0
      return {};
650
0
    }
651
652
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
653
0
    if (fileInfo == nullptr || fileInfo->os_desc == nullptr || fileInfo->os_desc_size == 0)
654
0
      return {};
655
656
0
    return std::string(fileInfo->os_desc, fileInfo->os_desc_size);
657
0
  }
658
659
  std::string PcapNgFileReaderDevice::getHardware() const
660
0
  {
661
0
    if (m_LightPcapNg == nullptr)
662
0
    {
663
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
664
0
      return {};
665
0
    }
666
667
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
668
0
    if (fileInfo == nullptr || fileInfo->hardware_desc == nullptr || fileInfo->hardware_desc_size == 0)
669
0
      return {};
670
671
0
    return std::string(fileInfo->hardware_desc, fileInfo->hardware_desc_size);
672
0
  }
673
674
  std::string PcapNgFileReaderDevice::getCaptureApplication() const
675
0
  {
676
0
    if (m_LightPcapNg == nullptr)
677
0
    {
678
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
679
0
      return {};
680
0
    }
681
682
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
683
0
    if (fileInfo == nullptr || fileInfo->user_app_desc == nullptr || fileInfo->user_app_desc_size == 0)
684
0
      return {};
685
686
0
    return std::string(fileInfo->user_app_desc, fileInfo->user_app_desc_size);
687
0
  }
688
689
  std::string PcapNgFileReaderDevice::getCaptureFileComment() const
690
0
  {
691
0
    if (m_LightPcapNg == nullptr)
692
0
    {
693
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
694
0
      return {};
695
0
    }
696
697
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
698
0
    if (fileInfo == nullptr || fileInfo->file_comment == nullptr || fileInfo->file_comment_size == 0)
699
0
      return {};
700
701
0
    return std::string(fileInfo->file_comment, fileInfo->file_comment_size);
702
0
  }
703
704
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
705
  // PcapNgFileWriterDevice members
706
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
707
708
  PcapNgFileWriterDevice::PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel)
709
3.75k
      : IFileWriterDevice(fileName)
710
3.75k
  {
711
3.75k
    m_LightPcapNg = nullptr;
712
3.75k
    m_CompressionLevel = compressionLevel;
713
3.75k
  }
714
715
  bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet, const std::string& comment)
716
24.1k
  {
717
24.1k
    if (m_LightPcapNg == nullptr)
718
0
    {
719
0
      PCPP_LOG_ERROR("Device not opened");
720
0
      reportPacketDropped();
721
0
      return false;
722
0
    }
723
724
24.1k
    if (!m_BpfWrapper.matches(packet))
725
0
    {
726
0
      return false;
727
0
    }
728
729
24.1k
    light_packet_header pktHeader;
730
24.1k
    pktHeader.captured_length = packet.getRawDataLen();
731
24.1k
    pktHeader.original_length = packet.getFrameLength();
732
24.1k
    pktHeader.timestamp = packet.getPacketTimeStamp();
733
24.1k
    pktHeader.data_link = static_cast<uint16_t>(packet.getLinkLayerType());
734
24.1k
    pktHeader.interface_id = 0;
735
24.1k
    if (!comment.empty())
736
0
    {
737
0
      pktHeader.comment = const_cast<char*>(comment.c_str());
738
0
      pktHeader.comment_length = static_cast<uint16_t>(comment.size());
739
0
    }
740
24.1k
    else
741
24.1k
    {
742
24.1k
      pktHeader.comment = nullptr;
743
24.1k
      pktHeader.comment_length = 0;
744
24.1k
    }
745
746
24.1k
    const uint8_t* pktData = packet.getRawData();
747
748
24.1k
    light_write_packet(toLightPcapNgT(m_LightPcapNg), &pktHeader, pktData);
749
24.1k
    reportPacketProcessed();
750
24.1k
    return true;
751
24.1k
  }
752
753
  bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet)
754
24.1k
  {
755
24.1k
    return writePacket(packet, std::string());
756
24.1k
  }
757
758
  bool PcapNgFileWriterDevice::writePackets(const RawPacketVector& packets)
759
0
  {
760
0
    for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++)
761
0
    {
762
0
      if (!writePacket(**iter))
763
0
        return false;
764
0
    }
765
766
0
    return true;
767
0
  }
768
769
  bool PcapNgFileWriterDevice::open()
770
0
  {
771
0
    return openWrite();
772
0
  }
773
774
  bool PcapNgFileWriterDevice::open(bool appendMode)
775
3.75k
  {
776
3.75k
    return appendMode ? openAppend() : openWrite();
777
3.75k
  }
778
779
  bool PcapNgFileWriterDevice::open(const std::string& os, const std::string& hardware, const std::string& captureApp,
780
                                    const std::string& fileComment)
781
0
  {
782
0
    PcapNgMetadata metadata;
783
0
    metadata.os = os;
784
0
    metadata.hardware = hardware;
785
0
    metadata.captureApplication = captureApp;
786
0
    metadata.comment = fileComment;
787
0
    return openWrite(&metadata);
788
0
  }
789
790
  bool PcapNgFileWriterDevice::openWrite(PcapNgMetadata const* metadata)
791
376
  {
792
    // TODO: Ambiguity in the API
793
    //   If the user calls open() and then open(true) - should we close the first one or report failure?
794
    //   Currently the method reports a success, but the opened device would not match the appendMode.
795
376
    if (m_LightPcapNg != nullptr)
796
0
    {
797
0
      PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do");
798
0
      return true;
799
0
    }
800
801
376
    resetStatisticCounters();
802
803
376
    light_pcapng_file_info* info;
804
376
    if (metadata == nullptr)
805
376
    {
806
376
      info = light_create_default_file_info();
807
376
    }
808
0
    else
809
0
    {
810
0
      info = light_create_file_info(metadata->os.c_str(), metadata->hardware.c_str(),
811
0
                                    metadata->captureApplication.c_str(), metadata->comment.c_str());
812
0
    }
813
814
376
    m_LightPcapNg = toLightPcapNgHandle(light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel));
815
376
    if (m_LightPcapNg == nullptr)
816
0
    {
817
0
      PCPP_LOG_ERROR("Error opening file writer device for file '"
818
0
                     << m_FileName << "': light_pcapng_open_write returned nullptr");
819
820
0
      light_free_file_info(info);
821
822
0
      m_DeviceOpened = false;
823
0
      return false;
824
0
    }
825
826
376
    m_DeviceOpened = true;
827
376
    PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully");
828
376
    return true;
829
376
  }
830
831
  bool PcapNgFileWriterDevice::openAppend()
832
3.37k
  {
833
    // TODO: Ambiguity in the API
834
    //   If the user calls open() and then open(true) - should we close the first one or report failure?
835
    //   Currently the method reports a success, but the opened device would not match the appendMode.
836
3.37k
    if (m_LightPcapNg != nullptr)
837
0
    {
838
0
      PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do");
839
0
      return true;
840
0
    }
841
842
3.37k
    resetStatisticCounters();
843
844
3.37k
    m_LightPcapNg = toLightPcapNgHandle(light_pcapng_open_append(m_FileName.c_str()));
845
3.37k
    if (m_LightPcapNg == nullptr)
846
0
    {
847
0
      PCPP_LOG_ERROR("Error opening file writer device in append mode for file '"
848
0
                     << m_FileName << "': light_pcapng_open_append returned nullptr");
849
0
      m_DeviceOpened = false;
850
0
      return false;
851
0
    }
852
853
3.37k
    m_DeviceOpened = true;
854
3.37k
    PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully");
855
3.37k
    return true;
856
3.37k
  }
857
858
  void PcapNgFileWriterDevice::flush()
859
0
  {
860
0
    if (!m_DeviceOpened || m_LightPcapNg == nullptr)
861
0
      return;
862
863
0
    light_pcapng_flush(toLightPcapNgT(m_LightPcapNg));
864
0
    PCPP_LOG_DEBUG("File writer flushed to file '" << m_FileName << "'");
865
0
  }
866
867
  void PcapNgFileWriterDevice::close()
868
6.27k
  {
869
6.27k
    if (m_LightPcapNg == nullptr)
870
2.52k
      return;
871
872
3.75k
    light_pcapng_close(toLightPcapNgT(m_LightPcapNg));
873
3.75k
    m_LightPcapNg = nullptr;
874
875
3.75k
    m_DeviceOpened = false;
876
3.75k
    PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'");
877
3.75k
  }
878
879
  bool PcapNgFileWriterDevice::setFilter(std::string filterAsString)
880
0
  {
881
0
    return m_BpfWrapper.setFilter(filterAsString);
882
0
  }
883
884
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
885
  // SnoopFileReaderDevice members
886
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
887
888
  SnoopFileReaderDevice::~SnoopFileReaderDevice()
889
216
  {
890
216
    m_snoopFile.close();
891
216
  }
892
893
  bool SnoopFileReaderDevice::open()
894
216
  {
895
216
    resetStatisticCounters();
896
897
216
    m_snoopFile.open(m_FileName.c_str(), std::ifstream::binary);
898
216
    if (!m_snoopFile.is_open())
899
0
    {
900
0
      PCPP_LOG_ERROR("Cannot open snoop reader device for filename '" << m_FileName << "'");
901
0
      m_snoopFile.close();
902
0
      return false;
903
0
    }
904
905
216
    snoop_file_header_t snoop_file_header;
906
216
    m_snoopFile.read((char*)&snoop_file_header, sizeof(snoop_file_header_t));
907
216
    if (!m_snoopFile)
908
2
    {
909
2
      PCPP_LOG_ERROR("Cannot read snoop file header for '" << m_FileName << "'");
910
2
      m_snoopFile.close();
911
2
      return false;
912
2
    }
913
914
214
    if (be64toh(snoop_file_header.identification_pattern) != 0x736e6f6f70000000 &&
915
214
        be32toh(snoop_file_header.version_number) == 2)
916
1
      return false;
917
918
    // From https://datatracker.ietf.org/doc/html/rfc1761
919
213
    static const pcpp::LinkLayerType snoop_encap[] = {
920
213
      LINKTYPE_ETHERNET,   /// IEEE 802.3
921
213
      LINKTYPE_NULL,       /// IEEE 802.4 Token Bus
922
213
      LINKTYPE_IEEE802_5,  /// IEEE 802.5
923
213
      LINKTYPE_NULL,       /// IEEE 802.6 Metro Net
924
213
      LINKTYPE_ETHERNET,   /// Ethernet
925
213
      LINKTYPE_C_HDLC,     /// HDLC
926
213
      LINKTYPE_NULL,       /// Character Synchronous, e.g. bisync
927
213
      LINKTYPE_NULL,       /// IBM Channel-to-Channel
928
213
      LINKTYPE_FDDI        /// FDDI
929
213
    };
930
213
    uint32_t datalink_type = be32toh(snoop_file_header.datalink_type);
931
213
    if (datalink_type > ARRAY_SIZE(snoop_encap) - 1)
932
46
    {
933
46
      PCPP_LOG_ERROR("Cannot read data link type for '" << m_FileName << "'");
934
46
      m_snoopFile.close();
935
46
      return false;
936
46
    }
937
938
167
    m_PcapLinkLayerType = snoop_encap[datalink_type];
939
940
167
    PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'");
941
167
    m_DeviceOpened = true;
942
167
    return true;
943
213
  }
944
945
  bool SnoopFileReaderDevice::getNextPacket(RawPacket& rawPacket)
946
734
  {
947
734
    if (m_DeviceOpened != true)
948
0
    {
949
0
      PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened");
950
0
      return false;
951
0
    }
952
734
    snoop_packet_header_t snoop_packet_header;
953
734
    m_snoopFile.read((char*)&snoop_packet_header, sizeof(snoop_packet_header_t));
954
734
    if (!m_snoopFile)
955
119
    {
956
119
      return false;
957
119
    }
958
615
    size_t packetSize = be32toh(snoop_packet_header.included_length);
959
615
    if (packetSize > 15000)
960
31
    {
961
31
      return false;
962
31
    }
963
584
    std::unique_ptr<char[]> packetData = std::make_unique<char[]>(packetSize);
964
584
    m_snoopFile.read(packetData.get(), packetSize);
965
584
    if (!m_snoopFile)
966
17
    {
967
17
      return false;
968
17
    }
969
567
    timespec ts = { static_cast<time_t>(be32toh(snoop_packet_header.time_sec)),
970
567
                  static_cast<long>(be32toh(snoop_packet_header.time_usec)) * 1000 };
971
567
    if (!rawPacket.setRawData((const uint8_t*)packetData.release(), packetSize, ts,
972
567
                              static_cast<LinkLayerType>(m_PcapLinkLayerType)))
973
0
    {
974
0
      PCPP_LOG_ERROR("Couldn't set data to raw packet");
975
0
      return false;
976
0
    }
977
567
    size_t pad = be32toh(snoop_packet_header.packet_record_length) -
978
567
                 (sizeof(snoop_packet_header_t) + be32toh(snoop_packet_header.included_length));
979
567
    m_snoopFile.ignore(pad);
980
567
    if (!m_snoopFile)
981
0
    {
982
0
      return false;
983
0
    }
984
985
567
    reportPacketProcessed();
986
567
    return true;
987
567
  }
988
989
  void SnoopFileReaderDevice::close()
990
17
  {
991
17
    m_snoopFile.close();
992
17
    m_DeviceOpened = false;
993
17
    PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'");
994
17
  }
995
}  // namespace pcpp