Coverage Report

Created: 2026-01-17 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Pcap++/src/PcapFileDevice.cpp
Line
Count
Source
1
22.4k
#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
3.68k
    {
22
3.68k
      return reinterpret_cast<internal::LightPcapNgHandle*>(pcapngHandle);
23
3.68k
    }
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
27.7k
    {
30
27.7k
      return reinterpret_cast<light_pcapng_t*>(pcapngHandle);
31
27.7k
    }
32
  }  // namespace
33
34
  template <typename T, size_t N> constexpr size_t ARRAY_SIZE(T (&)[N])
35
0
  {
36
0
    return N;
37
0
  }
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
8.13k
  IFileDevice::IFileDevice(const std::string& fileName) : IPcapDevice()
75
8.13k
  {
76
8.13k
    m_FileName = fileName;
77
8.13k
  }
78
79
  IFileDevice::~IFileDevice()
80
8.13k
  {
81
8.13k
    IFileDevice::close();
82
8.13k
  }
83
84
  std::string IFileDevice::getFileName() const
85
0
  {
86
0
    return m_FileName;
87
0
  }
88
89
  void IFileDevice::close()
90
8.13k
  {
91
8.13k
    if (m_PcapDescriptor != nullptr)
92
3.68k
    {
93
3.68k
      m_PcapDescriptor = nullptr;
94
3.68k
      PCPP_LOG_DEBUG("Successfully closed file reader device for filename '" << m_FileName << "'");
95
3.68k
    }
96
97
8.13k
    m_DeviceOpened = false;
98
8.13k
  }
99
100
  void IFileDevice::getStatistics(PcapStats& stats) const
101
2.48k
  {
102
2.48k
    PCPP_LOG_DEBUG("Statistics requested for file device for filename '" << m_FileName << "'");
103
2.48k
    stats.packetsRecv = m_NumOfPacketsProcessed;
104
2.48k
    stats.packetsDrop = m_NumOfPacketsDropped;
105
2.48k
    stats.packetsDropByInterface = 0;
106
2.48k
  }
107
108
  void IFileDevice::resetStatisticCounters()
109
8.13k
  {
110
8.13k
    m_NumOfPacketsProcessed = 0;
111
8.13k
    m_NumOfPacketsDropped = 0;
112
8.13k
  }
113
114
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
115
  // IFileReaderDevice members
116
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
117
118
4.45k
  IFileReaderDevice::IFileReaderDevice(const std::string& fileName) : IFileDevice(fileName)
119
4.45k
  {}
120
121
  IFileReaderDevice* IFileReaderDevice::getReader(const std::string& fileName)
122
4.45k
  {
123
4.45k
    const auto extensionPos = fileName.find_last_of('.');
124
4.45k
    const auto fileExtension = extensionPos != std::string::npos ? fileName.substr(extensionPos) : "";
125
126
4.45k
    if (fileExtension == ".pcapng" || fileExtension == ".zstd" || fileExtension == ".zst")
127
0
      return new PcapNgFileReaderDevice(fileName);
128
4.45k
    else if (fileExtension == ".snoop")
129
0
      return new SnoopFileReaderDevice(fileName);
130
131
4.45k
    return new PcapFileReaderDevice(fileName);
132
4.45k
  }
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
3.68k
  {
142
3.68k
    int numOfPacketsRead = 0;
143
144
6.16k
    for (; numOfPacketsToRead < 0 || numOfPacketsRead < numOfPacketsToRead; numOfPacketsRead++)
145
3.68k
    {
146
3.68k
      RawPacket* newPacket = new RawPacket();
147
3.68k
      bool packetRead = getNextPacket(*newPacket);
148
3.68k
      if (packetRead)
149
2.48k
      {
150
2.48k
        packetVec.pushBack(newPacket);
151
2.48k
      }
152
1.19k
      else
153
1.19k
      {
154
1.19k
        delete newPacket;
155
1.19k
        break;
156
1.19k
      }
157
3.68k
    }
158
159
3.68k
    return numOfPacketsRead;
160
3.68k
  }
161
162
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
163
  // IFileWriterDevice members
164
  // ~~~~~~~~~~~~~~~~~~~~~~~~~
165
166
3.68k
  IFileWriterDevice::IFileWriterDevice(const std::string& fileName) : IFileDevice(fileName)
167
3.68k
  {}
168
169
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
170
  // PcapFileReaderDevice members
171
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
172
173
  bool PcapFileReaderDevice::open()
174
4.45k
  {
175
4.45k
    resetStatisticCounters();
176
177
4.45k
    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.45k
    char errbuf[PCAP_ERRBUF_SIZE];
184
4.45k
#if defined(PCAP_TSTAMP_PRECISION_NANO)
185
4.45k
    auto pcapDescriptor = internal::PcapHandle(
186
4.45k
        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.45k
    if (pcapDescriptor == nullptr)
191
714
    {
192
714
      PCPP_LOG_ERROR("Cannot open file reader device for filename '" << m_FileName << "': " << errbuf);
193
714
      m_DeviceOpened = false;
194
714
      return false;
195
714
    }
196
197
3.74k
    int linkLayer = pcap_datalink(pcapDescriptor.get());
198
3.74k
    if (!RawPacket::isLinkTypeValid(linkLayer))
199
58
    {
200
58
      PCPP_LOG_ERROR("Invalid link layer (" << linkLayer << ") for reader device filename '" << m_FileName
201
58
                                            << "'");
202
58
      m_DeviceOpened = false;
203
58
      return false;
204
58
    }
205
206
3.68k
    m_PcapLinkLayerType = static_cast<LinkLayerType>(linkLayer);
207
208
3.68k
#if defined(PCAP_TSTAMP_PRECISION_NANO)
209
3.68k
    m_Precision = static_cast<FileTimestampPrecision>(pcap_get_tstamp_precision(pcapDescriptor.get()));
210
3.68k
    std::string precisionStr =
211
3.68k
        (m_Precision == FileTimestampPrecision::Nanoseconds) ? "nanoseconds" : "microseconds";
212
#else
213
    m_Precision = FileTimestampPrecision::Microseconds;
214
    std::string precisionStr = "microseconds";
215
#endif
216
3.68k
    PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "' with precision "
217
3.68k
                                                                           << precisionStr);
218
3.68k
    m_PcapDescriptor = std::move(pcapDescriptor);
219
3.68k
    m_DeviceOpened = true;
220
3.68k
    return true;
221
3.74k
  }
222
223
  bool PcapFileReaderDevice::isNanoSecondPrecisionSupported()
224
0
  {
225
0
    return checkNanoSupport();
226
0
  }
227
228
  bool PcapFileReaderDevice::getNextPacket(RawPacket& rawPacket)
229
27.7k
  {
230
27.7k
    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.7k
    pcap_pkthdr pkthdr;
236
27.7k
    const uint8_t* pPacketData = pcap_next(m_PcapDescriptor.get(), &pkthdr);
237
27.7k
    if (pPacketData == nullptr)
238
3.68k
    {
239
3.68k
      PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file");
240
3.68k
      return false;
241
3.68k
    }
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
0
      : IFileWriterDevice(fileName)
268
0
  {
269
0
    m_PcapDumpHandler = nullptr;
270
0
    m_PcapLinkLayerType = linkLayerType;
271
0
    m_AppendMode = false;
272
0
#if defined(PCAP_TSTAMP_PRECISION_NANO)
273
0
    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
0
    m_File = nullptr;
284
0
  }
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
0
  {
297
0
    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
0
    if (packet.getLinkLayerType() != m_PcapLinkLayerType)
305
0
    {
306
0
      PCPP_LOG_ERROR("Cannot write a packet with a different link layer type");
307
0
      reportPacketDropped();
308
0
      return false;
309
0
    }
310
311
0
    pcap_pkthdr pktHdr;
312
0
    pktHdr.caplen = packet.getRawDataLen();
313
0
    pktHdr.len = packet.getFrameLength();
314
0
    timespec packet_timestamp = packet.getPacketTimeStamp();
315
0
#if defined(PCAP_TSTAMP_PRECISION_NANO)
316
0
    if (m_Precision != FileTimestampPrecision::Nanoseconds)
317
0
    {
318
0
      pktHdr.ts = internal::toTimeval(packet_timestamp);
319
0
    }
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
0
    if (!m_AppendMode)
329
0
      pcap_dump(reinterpret_cast<uint8_t*>(m_PcapDumpHandler), &pktHdr, packet.getRawData());
330
0
    else
331
0
    {
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
0
      packet_header pktHdrTemp;
342
0
      pktHdrTemp.tv_sec = pktHdr.ts.tv_sec;
343
0
      pktHdrTemp.tv_usec = pktHdr.ts.tv_usec;
344
0
      pktHdrTemp.caplen = pktHdr.caplen;
345
0
      pktHdrTemp.len = pktHdr.len;
346
0
      fwrite(&pktHdrTemp, sizeof(pktHdrTemp), 1, m_File);
347
0
      fwrite(packet.getRawData(), pktHdrTemp.caplen, 1, m_File);
348
0
    }
349
0
    PCPP_LOG_DEBUG("Packet written successfully to '" << m_FileName << "'");
350
0
    reportPacketProcessed();
351
0
    return true;
352
0
  }
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
0
  {
377
0
    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
0
    if (appendMode)
386
0
    {
387
0
      return openAppend();
388
0
    }
389
0
    else
390
0
    {
391
0
      return openWrite();
392
0
    }
393
0
  }
394
395
  bool PcapFileWriterDevice::openWrite()
396
0
  {
397
0
    m_AppendMode = false;
398
399
0
    switch (m_PcapLinkLayerType)
400
0
    {
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
0
    default:
407
0
      break;
408
0
    }
409
410
0
    resetStatisticCounters();
411
412
0
#if defined(PCAP_TSTAMP_PRECISION_NANO)
413
0
    auto pcapDescriptor = internal::PcapHandle(pcap_open_dead_with_tstamp_precision(
414
0
        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
0
    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
0
    m_PcapDumpHandler = pcap_dump_open(pcapDescriptor.get(), m_FileName.c_str());
427
0
    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
0
    m_PcapDescriptor = std::move(pcapDescriptor);
437
0
    m_DeviceOpened = true;
438
0
    PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully");
439
0
    return true;
440
0
  }
441
442
  bool PcapFileWriterDevice::openAppend()
443
0
  {
444
0
    m_AppendMode = true;
445
446
0
#if !defined(_WIN32)
447
0
    m_File = fopen(m_FileName.c_str(), "r+");
448
#else
449
    m_File = fopen(m_FileName.c_str(), "rb+");
450
#endif
451
452
0
    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
0
    pcap_file_header pcapFileHeader;
459
0
    int amountRead = fread(&pcapFileHeader, 1, sizeof(pcapFileHeader), m_File);
460
0
    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
0
    LinkLayerType linkLayerType = static_cast<LinkLayerType>(pcapFileHeader.linktype);
472
0
    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
0
    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
0
    m_PcapDumpHandler = reinterpret_cast<pcap_dumper_t*>(m_File);
489
490
0
    m_DeviceOpened = true;
491
0
    PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully in append mode");
492
0
    return true;
493
0
  }
494
495
  void PcapFileWriterDevice::flush()
496
0
  {
497
0
    if (!m_DeviceOpened)
498
0
      return;
499
500
0
    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
0
    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
0
  }
510
511
  void PcapFileWriterDevice::close()
512
0
  {
513
0
    if (!m_DeviceOpened)
514
0
      return;
515
516
0
    flush();
517
518
0
    IFileDevice::close();
519
520
0
    if (!m_AppendMode && m_PcapDumpHandler != nullptr)
521
0
    {
522
0
      pcap_dump_close(m_PcapDumpHandler);
523
0
    }
524
0
    else if (m_AppendMode && m_File != nullptr)
525
0
    {
526
      // in append mode it's impossible to use pcap_dump_close, see comment above pcap_dump
527
0
      fclose(m_File);
528
0
    }
529
530
0
    m_PcapDumpHandler = nullptr;
531
0
    m_File = nullptr;
532
0
    PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'");
533
0
  }
534
535
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
536
  // PcapNgFileReaderDevice members
537
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
538
539
0
  PcapNgFileReaderDevice::PcapNgFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName)
540
0
  {
541
0
    m_LightPcapNg = nullptr;
542
0
  }
543
544
  bool PcapNgFileReaderDevice::open()
545
0
  {
546
0
    resetStatisticCounters();
547
548
0
    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
0
    m_LightPcapNg = toLightPcapNgHandle(light_pcapng_open_read(m_FileName.c_str(), LIGHT_FALSE));
555
0
    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
0
    PCPP_LOG_DEBUG("Successfully opened pcapng reader device for filename '" << m_FileName << "'");
563
0
    m_DeviceOpened = true;
564
0
    return true;
565
0
  }
566
567
  bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket, std::string& packetComment)
568
0
  {
569
0
    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
0
    light_packet_header pktHeader;
576
0
    const uint8_t* pktData = nullptr;
577
578
0
    if (!light_get_next_packet(toLightPcapNgT(m_LightPcapNg), &pktHeader, &pktData))
579
0
    {
580
0
      PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file");
581
0
      return false;
582
0
    }
583
584
0
    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
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::doUpdateFilter(std::string const* filterAsString)
628
0
  {
629
0
    if (filterAsString == nullptr)
630
0
    {
631
0
      return m_BpfWrapper.setFilter("");
632
0
    }
633
634
0
    return m_BpfWrapper.setFilter(*filterAsString);
635
0
  }
636
637
  void PcapNgFileReaderDevice::close()
638
0
  {
639
0
    if (m_LightPcapNg == nullptr)
640
0
      return;
641
642
0
    light_pcapng_close(toLightPcapNgT(m_LightPcapNg));
643
0
    m_LightPcapNg = nullptr;
644
645
0
    m_DeviceOpened = false;
646
0
    PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'");
647
0
  }
648
649
  std::string PcapNgFileReaderDevice::getOS() const
650
0
  {
651
0
    if (m_LightPcapNg == nullptr)
652
0
    {
653
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
654
0
      return {};
655
0
    }
656
657
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
658
0
    if (fileInfo == nullptr || fileInfo->os_desc == nullptr || fileInfo->os_desc_size == 0)
659
0
      return {};
660
661
0
    return std::string(fileInfo->os_desc, fileInfo->os_desc_size);
662
0
  }
663
664
  std::string PcapNgFileReaderDevice::getHardware() const
665
0
  {
666
0
    if (m_LightPcapNg == nullptr)
667
0
    {
668
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
669
0
      return {};
670
0
    }
671
672
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
673
0
    if (fileInfo == nullptr || fileInfo->hardware_desc == nullptr || fileInfo->hardware_desc_size == 0)
674
0
      return {};
675
676
0
    return std::string(fileInfo->hardware_desc, fileInfo->hardware_desc_size);
677
0
  }
678
679
  std::string PcapNgFileReaderDevice::getCaptureApplication() const
680
0
  {
681
0
    if (m_LightPcapNg == nullptr)
682
0
    {
683
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
684
0
      return {};
685
0
    }
686
687
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
688
0
    if (fileInfo == nullptr || fileInfo->user_app_desc == nullptr || fileInfo->user_app_desc_size == 0)
689
0
      return {};
690
691
0
    return std::string(fileInfo->user_app_desc, fileInfo->user_app_desc_size);
692
0
  }
693
694
  std::string PcapNgFileReaderDevice::getCaptureFileComment() const
695
0
  {
696
0
    if (m_LightPcapNg == nullptr)
697
0
    {
698
0
      PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened");
699
0
      return {};
700
0
    }
701
702
0
    light_pcapng_file_info* fileInfo = light_pcang_get_file_info(toLightPcapNgT(m_LightPcapNg));
703
0
    if (fileInfo == nullptr || fileInfo->file_comment == nullptr || fileInfo->file_comment_size == 0)
704
0
      return {};
705
706
0
    return std::string(fileInfo->file_comment, fileInfo->file_comment_size);
707
0
  }
708
709
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
710
  // PcapNgFileWriterDevice members
711
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
712
713
  PcapNgFileWriterDevice::PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel)
714
3.68k
      : IFileWriterDevice(fileName)
715
3.68k
  {
716
3.68k
    m_LightPcapNg = nullptr;
717
3.68k
    m_CompressionLevel = compressionLevel;
718
3.68k
  }
719
720
  bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet, const std::string& comment)
721
24.1k
  {
722
24.1k
    if (m_LightPcapNg == nullptr)
723
0
    {
724
0
      PCPP_LOG_ERROR("Device not opened");
725
0
      reportPacketDropped();
726
0
      return false;
727
0
    }
728
729
24.1k
    if (!m_BpfWrapper.matches(packet))
730
0
    {
731
0
      return false;
732
0
    }
733
734
24.1k
    light_packet_header pktHeader;
735
24.1k
    pktHeader.captured_length = packet.getRawDataLen();
736
24.1k
    pktHeader.original_length = packet.getFrameLength();
737
24.1k
    pktHeader.timestamp = packet.getPacketTimeStamp();
738
24.1k
    pktHeader.data_link = static_cast<uint16_t>(packet.getLinkLayerType());
739
24.1k
    pktHeader.interface_id = 0;
740
24.1k
    if (!comment.empty())
741
0
    {
742
0
      pktHeader.comment = const_cast<char*>(comment.c_str());
743
0
      pktHeader.comment_length = static_cast<uint16_t>(comment.size());
744
0
    }
745
24.1k
    else
746
24.1k
    {
747
24.1k
      pktHeader.comment = nullptr;
748
24.1k
      pktHeader.comment_length = 0;
749
24.1k
    }
750
751
24.1k
    const uint8_t* pktData = packet.getRawData();
752
753
24.1k
    light_write_packet(toLightPcapNgT(m_LightPcapNg), &pktHeader, pktData);
754
24.1k
    reportPacketProcessed();
755
24.1k
    return true;
756
24.1k
  }
757
758
  bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet)
759
24.1k
  {
760
24.1k
    return writePacket(packet, std::string());
761
24.1k
  }
762
763
  bool PcapNgFileWriterDevice::writePackets(const RawPacketVector& packets)
764
0
  {
765
0
    for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++)
766
0
    {
767
0
      if (!writePacket(**iter))
768
0
        return false;
769
0
    }
770
771
0
    return true;
772
0
  }
773
774
  bool PcapNgFileWriterDevice::open()
775
0
  {
776
0
    return openWrite();
777
0
  }
778
779
  bool PcapNgFileWriterDevice::open(bool appendMode)
780
3.68k
  {
781
3.68k
    return appendMode ? openAppend() : openWrite();
782
3.68k
  }
783
784
  bool PcapNgFileWriterDevice::open(const std::string& os, const std::string& hardware, const std::string& captureApp,
785
                                    const std::string& fileComment)
786
0
  {
787
0
    PcapNgMetadata metadata;
788
0
    metadata.os = os;
789
0
    metadata.hardware = hardware;
790
0
    metadata.captureApplication = captureApp;
791
0
    metadata.comment = fileComment;
792
0
    return openWrite(&metadata);
793
0
  }
794
795
  bool PcapNgFileWriterDevice::openWrite(PcapNgMetadata const* metadata)
796
369
  {
797
    // TODO: Ambiguity in the API
798
    //   If the user calls open() and then open(true) - should we close the first one or report failure?
799
    //   Currently the method reports a success, but the opened device would not match the appendMode.
800
369
    if (m_LightPcapNg != nullptr)
801
0
    {
802
0
      PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do");
803
0
      return true;
804
0
    }
805
806
369
    resetStatisticCounters();
807
808
369
    light_pcapng_file_info* info;
809
369
    if (metadata == nullptr)
810
369
    {
811
369
      info = light_create_default_file_info();
812
369
    }
813
0
    else
814
0
    {
815
0
      info = light_create_file_info(metadata->os.c_str(), metadata->hardware.c_str(),
816
0
                                    metadata->captureApplication.c_str(), metadata->comment.c_str());
817
0
    }
818
819
369
    m_LightPcapNg = toLightPcapNgHandle(light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel));
820
369
    if (m_LightPcapNg == nullptr)
821
0
    {
822
0
      PCPP_LOG_ERROR("Error opening file writer device for file '"
823
0
                     << m_FileName << "': light_pcapng_open_write returned nullptr");
824
825
0
      light_free_file_info(info);
826
827
0
      m_DeviceOpened = false;
828
0
      return false;
829
0
    }
830
831
369
    m_DeviceOpened = true;
832
369
    PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully");
833
369
    return true;
834
369
  }
835
836
  bool PcapNgFileWriterDevice::openAppend()
837
3.31k
  {
838
    // TODO: Ambiguity in the API
839
    //   If the user calls open() and then open(true) - should we close the first one or report failure?
840
    //   Currently the method reports a success, but the opened device would not match the appendMode.
841
3.31k
    if (m_LightPcapNg != nullptr)
842
0
    {
843
0
      PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do");
844
0
      return true;
845
0
    }
846
847
3.31k
    resetStatisticCounters();
848
849
3.31k
    m_LightPcapNg = toLightPcapNgHandle(light_pcapng_open_append(m_FileName.c_str()));
850
3.31k
    if (m_LightPcapNg == nullptr)
851
0
    {
852
0
      PCPP_LOG_ERROR("Error opening file writer device in append mode for file '"
853
0
                     << m_FileName << "': light_pcapng_open_append returned nullptr");
854
0
      m_DeviceOpened = false;
855
0
      return false;
856
0
    }
857
858
3.31k
    m_DeviceOpened = true;
859
3.31k
    PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully");
860
3.31k
    return true;
861
3.31k
  }
862
863
  void PcapNgFileWriterDevice::flush()
864
0
  {
865
0
    if (!m_DeviceOpened || m_LightPcapNg == nullptr)
866
0
      return;
867
868
0
    light_pcapng_flush(toLightPcapNgT(m_LightPcapNg));
869
0
    PCPP_LOG_DEBUG("File writer flushed to file '" << m_FileName << "'");
870
0
  }
871
872
  void PcapNgFileWriterDevice::close()
873
6.16k
  {
874
6.16k
    if (m_LightPcapNg == nullptr)
875
2.48k
      return;
876
877
3.68k
    light_pcapng_close(toLightPcapNgT(m_LightPcapNg));
878
3.68k
    m_LightPcapNg = nullptr;
879
880
3.68k
    m_DeviceOpened = false;
881
3.68k
    PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'");
882
3.68k
  }
883
884
  bool PcapNgFileWriterDevice::doUpdateFilter(std::string const* filterAsString)
885
0
  {
886
0
    if (filterAsString == nullptr)
887
0
    {
888
0
      return m_BpfWrapper.setFilter("");
889
0
    }
890
891
0
    return m_BpfWrapper.setFilter(*filterAsString);
892
0
  }
893
894
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
895
  // SnoopFileReaderDevice members
896
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
897
898
  SnoopFileReaderDevice::~SnoopFileReaderDevice()
899
0
  {
900
0
    m_snoopFile.close();
901
0
  }
902
903
  bool SnoopFileReaderDevice::open()
904
0
  {
905
0
    resetStatisticCounters();
906
907
0
    m_snoopFile.open(m_FileName.c_str(), std::ifstream::binary);
908
0
    if (!m_snoopFile.is_open())
909
0
    {
910
0
      PCPP_LOG_ERROR("Cannot open snoop reader device for filename '" << m_FileName << "'");
911
0
      m_snoopFile.close();
912
0
      return false;
913
0
    }
914
915
0
    snoop_file_header_t snoop_file_header;
916
0
    m_snoopFile.read((char*)&snoop_file_header, sizeof(snoop_file_header_t));
917
0
    if (!m_snoopFile)
918
0
    {
919
0
      PCPP_LOG_ERROR("Cannot read snoop file header for '" << m_FileName << "'");
920
0
      m_snoopFile.close();
921
0
      return false;
922
0
    }
923
924
0
    if (be64toh(snoop_file_header.identification_pattern) != 0x736e6f6f70000000 &&
925
0
        be32toh(snoop_file_header.version_number) == 2)
926
0
      return false;
927
928
    // From https://datatracker.ietf.org/doc/html/rfc1761
929
0
    static const pcpp::LinkLayerType snoop_encap[] = {
930
0
      LINKTYPE_ETHERNET,   /// IEEE 802.3
931
0
      LINKTYPE_NULL,       /// IEEE 802.4 Token Bus
932
0
      LINKTYPE_IEEE802_5,  /// IEEE 802.5
933
0
      LINKTYPE_NULL,       /// IEEE 802.6 Metro Net
934
0
      LINKTYPE_ETHERNET,   /// Ethernet
935
0
      LINKTYPE_C_HDLC,     /// HDLC
936
0
      LINKTYPE_NULL,       /// Character Synchronous, e.g. bisync
937
0
      LINKTYPE_NULL,       /// IBM Channel-to-Channel
938
0
      LINKTYPE_FDDI        /// FDDI
939
0
    };
940
0
    uint32_t datalink_type = be32toh(snoop_file_header.datalink_type);
941
0
    if (datalink_type > ARRAY_SIZE(snoop_encap) - 1)
942
0
    {
943
0
      PCPP_LOG_ERROR("Cannot read data link type for '" << m_FileName << "'");
944
0
      m_snoopFile.close();
945
0
      return false;
946
0
    }
947
948
0
    m_PcapLinkLayerType = snoop_encap[datalink_type];
949
950
0
    PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'");
951
0
    m_DeviceOpened = true;
952
0
    return true;
953
0
  }
954
955
  bool SnoopFileReaderDevice::getNextPacket(RawPacket& rawPacket)
956
0
  {
957
0
    if (m_DeviceOpened != true)
958
0
    {
959
0
      PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened");
960
0
      return false;
961
0
    }
962
0
    snoop_packet_header_t snoop_packet_header;
963
0
    m_snoopFile.read((char*)&snoop_packet_header, sizeof(snoop_packet_header_t));
964
0
    if (!m_snoopFile)
965
0
    {
966
0
      return false;
967
0
    }
968
0
    size_t packetSize = be32toh(snoop_packet_header.included_length);
969
0
    if (packetSize > 15000)
970
0
    {
971
0
      return false;
972
0
    }
973
0
    std::unique_ptr<char[]> packetData = std::make_unique<char[]>(packetSize);
974
0
    m_snoopFile.read(packetData.get(), packetSize);
975
0
    if (!m_snoopFile)
976
0
    {
977
0
      return false;
978
0
    }
979
0
    timespec ts = { static_cast<time_t>(be32toh(snoop_packet_header.time_sec)),
980
0
                  static_cast<long>(be32toh(snoop_packet_header.time_usec)) * 1000 };
981
0
    if (!rawPacket.setRawData((const uint8_t*)packetData.release(), packetSize, ts,
982
0
                              static_cast<LinkLayerType>(m_PcapLinkLayerType)))
983
0
    {
984
0
      PCPP_LOG_ERROR("Couldn't set data to raw packet");
985
0
      return false;
986
0
    }
987
0
    size_t pad = be32toh(snoop_packet_header.packet_record_length) -
988
0
                 (sizeof(snoop_packet_header_t) + be32toh(snoop_packet_header.included_length));
989
0
    m_snoopFile.ignore(pad);
990
0
    if (!m_snoopFile)
991
0
    {
992
0
      return false;
993
0
    }
994
995
0
    reportPacketProcessed();
996
0
    return true;
997
0
  }
998
999
  void SnoopFileReaderDevice::close()
1000
0
  {
1001
0
    m_snoopFile.close();
1002
0
    m_DeviceOpened = false;
1003
0
    PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'");
1004
0
  }
1005
}  // namespace pcpp