Coverage Report

Created: 2025-10-10 06:28

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