1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
#include <doctest/doctest.h>
#include <cstdio>
#include <vector>
#include "packeteer/pcapng/reader.hpp"
#include "packeteer/pcapng/writer.hpp"
using namespace packeteer::pcapng;
TEST_CASE("pcapng writer/reader round-trip a single packet") {
std::FILE* f = std::tmpfile();
REQUIRE(f != nullptr);
Writer writer(f);
writer.write_section_header();
writer.write_interface_description(65535, kLinkTypeEthernet);
std::vector<unsigned char> packet_data = {0xDE, 0xAD, 0xBE, 0xEF, 0x00};
writer.write_packet(/*interface_id=*/0, /*ts_sec=*/1700000000, /*ts_usec=*/123456,
packet_data, /*original_len=*/5);
std::fflush(f);
std::fseek(f, 0, SEEK_SET);
Reader reader(f);
auto record = reader.next_packet();
REQUIRE(record.has_value());
CHECK(record->interface_id == 0);
CHECK(record->timestamp_us == 1700000000ULL * 1'000'000ULL + 123456ULL);
CHECK(record->original_len == 5);
CHECK(record->data == packet_data);
CHECK_FALSE(reader.next_packet().has_value()); // only one packet was written
std::fclose(f);
}
TEST_CASE("Reader::link_type reflects the IDB, populated by the time the first packet returns") {
std::FILE* f = std::tmpfile();
REQUIRE(f != nullptr);
Writer writer(f);
writer.write_section_header();
writer.write_interface_description(65535, /*link_type=*/12); // DLT_RAW, arbitrary for this test
std::vector<unsigned char> data = {0x01};
writer.write_packet(0, 1, 0, data, 1);
std::fflush(f);
std::fseek(f, 0, SEEK_SET);
Reader reader(f);
CHECK_FALSE(reader.link_type().has_value()); // nothing read yet
auto record = reader.next_packet();
REQUIRE(record.has_value());
REQUIRE(reader.link_type().has_value());
CHECK(*reader.link_type() == 12);
std::fclose(f);
}
TEST_CASE("pcapng writer/reader round-trip multiple packets in order") {
std::FILE* f = std::tmpfile();
REQUIRE(f != nullptr);
Writer writer(f);
writer.write_section_header();
writer.write_interface_description(65535, kLinkTypeEthernet);
for (unsigned char i = 0; i < 5; ++i) {
std::vector<unsigned char> data = {i};
writer.write_packet(0, 1700000000 + i, 0, data, 1);
}
std::fflush(f);
std::fseek(f, 0, SEEK_SET);
Reader reader(f);
int count = 0;
while (auto record = reader.next_packet()) {
REQUIRE(record->data.size() == 1);
CHECK(record->data[0] == static_cast<unsigned char>(count));
++count;
}
CHECK(count == 5);
std::fclose(f);
}
TEST_CASE("pcapng writer pads packet data to a 4-byte boundary without corrupting the next block") {
std::FILE* f = std::tmpfile();
REQUIRE(f != nullptr);
Writer writer(f);
writer.write_section_header();
writer.write_interface_description(65535, kLinkTypeEthernet);
// 3 bytes of packet data forces padding - the case most likely to
// misalign the following block if the padding math is wrong.
std::vector<unsigned char> first = {0x01, 0x02, 0x03};
std::vector<unsigned char> second = {0xAA, 0xBB};
writer.write_packet(0, 1, 0, first, 3);
writer.write_packet(0, 2, 0, second, 2);
std::fflush(f);
std::fseek(f, 0, SEEK_SET);
Reader reader(f);
auto r1 = reader.next_packet();
REQUIRE(r1.has_value());
CHECK(r1->data == first);
auto r2 = reader.next_packet();
REQUIRE(r2.has_value());
CHECK(r2->data == second);
std::fclose(f);
}
TEST_CASE("Reader rejects a block claiming an implausibly large body instead of allocating it") {
// Found by fuzzing (fuzz/fuzz_pcapng_reader.cpp): total_len is an
// untrusted 32-bit value straight from the file. A block claiming
// ~4GB used to be handed straight to `std::vector` before a single
// body byte was read, OOM-crashing the process on a corrupt or
// hostile file. This constructs exactly that: a valid-looking
// block type, followed by a total_len far beyond anything our own
// writer would ever produce.
std::FILE* f = std::tmpfile();
REQUIRE(f != nullptr);
std::uint8_t block[8];
block[0] = 0x06; block[1] = 0x00; block[2] = 0x00; block[3] = 0x00; // EPB
block[4] = 0xFF; block[5] = 0xFF; block[6] = 0xFF; block[7] = 0x7F; // total_len ~2GB
std::fwrite(block, 1, sizeof(block), f);
std::fflush(f);
std::fseek(f, 0, SEEK_SET);
Reader reader(f);
CHECK_FALSE(reader.next_packet().has_value()); // rejected, not an OOM attempt
std::fclose(f);
}
|