Coverage Report

Created: 2025-07-11 07:47

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