Skip to content

Commit bdec00b

Browse files
authored
Add constructFromFactory API to accommodate parsing layers from custom functions. (#2052)
* Add new API to allow constructing new layers from factory functions. * Update docs. * Updated usages of setNextLayer. * Lint * Fix docs. * Suppress checkers report as it floods local runs. * Suppress false positive.
1 parent e435a1a commit bdec00b

File tree

10 files changed

+153
-34
lines changed

10 files changed

+153
-34
lines changed

Packet++/header/Layer.h

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,61 @@ namespace pcpp
301301
return newLayer;
302302
}
303303

304+
/// @brief Construct the next layer in the protocol stack using a factory functor.
305+
///
306+
/// No validation is performed on the data, outside of what the factory functor may perform.
307+
/// If the factory returns a nullptr, no next layer is set.
308+
///
309+
/// The factory functor is expected to have the following signature:
310+
/// Layer* factoryFn(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ...);
311+
///
312+
/// This overload infers the Packet from the current layer.
313+
///
314+
/// @tparam TFactory The factory functor type.
315+
/// @tparam ...Args Parameter pack for extra arguments to pass to the factory functor.
316+
/// @param[in] factoryFn The factory functor to create the layer.
317+
/// @param[in] data The data to construct the layer from
318+
/// @param[in] dataLen The length of the data
319+
/// @param[in] extraArgs Extra arguments to be forwarded to the factory.
320+
/// @return The return value of the factory functor.
321+
template <typename TFactory, typename... Args>
322+
Layer* constructNextLayerFromFactory(TFactory factoryFn, uint8_t* data, size_t dataLen, Args&&... extraArgs)
323+
{
324+
return constructNextLayerFromFactory<TFactory>(factoryFn, data, dataLen, getAttachedPacket(),
325+
std::forward<Args>(extraArgs)...);
326+
}
327+
328+
/// @brief Construct the next layer in the protocol stack using a factory functor.
329+
///
330+
/// No validation is performed on the data, outside of what the factory functor may perform.
331+
/// If the factory returns a nullptr, no next layer is set.
332+
///
333+
/// The factory functor is expected to have the following signature:
334+
/// Layer* factoryFn(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ...);
335+
///
336+
/// @tparam TFactory The factory functor type.
337+
/// @tparam ...Args Parameter pack for extra arguments to pass to the factory functor.
338+
/// @param[in] factoryFn The factory functor to create the layer.
339+
/// @param[in] data The data to construct the layer from
340+
/// @param[in] dataLen The length of the data
341+
/// @param[in] packet The packet the layer belongs to
342+
/// @param[in] extraArgs Extra arguments to be forwarded to the factory.
343+
/// @return The return value of the factory functor.
344+
template <typename TFactory, typename... Args>
345+
Layer* constructNextLayerFromFactory(TFactory factoryFn, uint8_t* data, size_t dataLen, Packet* packet,
346+
Args&&... extraArgs)
347+
{
348+
if (hasNextLayer())
349+
{
350+
throw std::runtime_error("Next layer already exists");
351+
}
352+
353+
// cppcheck-suppress redundantInitialization
354+
Layer* newLayer = factoryFn(data, dataLen, this, packet, std::forward<Args>(extraArgs)...);
355+
setNextLayer(newLayer);
356+
return newLayer;
357+
}
358+
304359
/// Try to construct the next layer in the protocol stack.
305360
///
306361
/// This overload infers the Packet from the current layer.
@@ -356,6 +411,8 @@ namespace pcpp
356411
/// @param[in] dataLen The length of the data
357412
/// @param[in] extraArgs Extra arguments to be forwarded to the layer constructor of T
358413
/// @return The constructed layer of type T or TFallback
414+
/// @remarks The parameters extraArgs are forwarded to the factory function, but not to the TFallback
415+
/// constructor.
359416
template <typename T, typename TFallback, typename... Args>
360417
Layer* tryConstructNextLayerWithFallback(uint8_t* data, size_t dataLen, Args&&... extraArgs)
361418
{
@@ -376,6 +433,8 @@ namespace pcpp
376433
/// @param[in] packet The packet the layer belongs to
377434
/// @param[in] extraArgs Extra arguments to be forwarded to the layer constructor of T
378435
/// @return The constructed layer of type T or TFallback
436+
/// @remarks The parameters extraArgs are forwarded to the factory function, but not to the TFallback
437+
/// constructor.
379438
template <typename T, typename TFallback, typename... Args>
380439
Layer* tryConstructNextLayerWithFallback(uint8_t* data, size_t dataLen, Packet* packet, Args&&... extraArgs)
381440
{
@@ -387,6 +446,71 @@ namespace pcpp
387446
return constructNextLayer<TFallback>(data, dataLen, packet);
388447
}
389448

449+
/// @brief Try to construct the next layer in the protocol stack using a factory functor with a fallback option.
450+
///
451+
/// The method will attempt to construct the next layer using the provided factory function.
452+
/// If the factory function returns nullptr, indicating failure to create the layer, the method will then
453+
/// construct a layer of type TFallback.
454+
///
455+
/// The factory functor is expected to have the following signature:
456+
/// Layer* factoryFn(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ...);
457+
///
458+
/// This overload infers the Packet from the current layer.
459+
///
460+
/// @tparam TFallback The fallback layer type to construct if the factory fails.
461+
/// @tparam TFactory The factory functor type.
462+
/// @tparam ...Args Parameter pack for extra arguments to pass to the factory functor.
463+
/// @param[in] factoryFn The factory functor to create the layer.
464+
/// @param[in] data The data to construct the layer from
465+
/// @param[in] dataLen The length of the data
466+
/// @param[in] extraArgs Extra arguments to be forwarded to the factory.
467+
/// @return The return value of the factory functor.
468+
/// @remarks The parameters extraArgs are forwarded to the factory function, but not to the TFallback
469+
/// constructor.
470+
template <typename TFallback, typename TFactory, typename... Args>
471+
Layer* tryConstructNextLayerFromFactoryWithFallback(TFactory factoryFn, uint8_t* data, size_t dataLen,
472+
Args&&... extraArgs)
473+
{
474+
// Note that the fallback is first to allow template argument deduction of the factory type.
475+
return tryConstructNextLayerFromFactoryWithFallback<TFallback, TFactory>(
476+
factoryFn, data, dataLen, getAttachedPacket(), std::forward<Args>(extraArgs)...);
477+
}
478+
479+
/// @brief Try to construct the next layer in the protocol stack using a factory functor with a fallback option.
480+
///
481+
/// The method will attempt to construct the next layer using the provided factory function.
482+
/// If the factory function returns nullptr, indicating failure to create the layer, the method will then
483+
/// construct a layer of type TFallback.
484+
///
485+
/// The factory functor is expected to have the following signature:
486+
/// Layer* factoryFn(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ...);
487+
///
488+
/// @tparam TFallback The fallback layer type to construct if the factory fails.
489+
/// @tparam TFactory The factory functor type.
490+
/// @tparam ...Args Parameter pack for extra arguments to pass to the factory functor.
491+
/// @param[in] factoryFn The factory functor to create the layer.
492+
/// @param[in] data The data to construct the layer from
493+
/// @param[in] dataLen The length of the data
494+
/// @param[in] packet The packet the layer belongs to
495+
/// @param[in] extraArgs Extra arguments to be forwarded to the factory.
496+
/// @return The return value of the factory functor.
497+
/// @remarks The parameters extraArgs are forwarded to the factory function, but not to the TFallback
498+
/// constructor.
499+
template <typename TFallback, typename TFactory, typename... Args>
500+
Layer* tryConstructNextLayerFromFactoryWithFallback(TFactory factoryFn, uint8_t* data, size_t dataLen,
501+
Packet* packet, Args&&... extraArgs)
502+
{
503+
auto nextLayer = constructNextLayerFromFactory<TFactory>(factoryFn, data, dataLen, packet,
504+
std::forward<Args>(extraArgs)...);
505+
if (nextLayer != nullptr)
506+
{
507+
return nextLayer;
508+
}
509+
510+
// factory failed, construct fallback layer
511+
return constructNextLayer<TFallback>(data, dataLen, packet);
512+
}
513+
390514
/// @brief Check if the data is large enough to reinterpret as a type
391515
///
392516
/// The data must be non-null and at least as large as the type

Packet++/src/BgpLayer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ namespace pcpp
8787
uint8_t* payload = m_Data + headerLen;
8888
size_t payloadLen = m_DataLen - headerLen;
8989

90-
setNextLayer(BgpLayer::parseBgpLayer(payload, payloadLen, this, getAttachedPacket()));
90+
constructNextLayerFromFactory(BgpLayer::parseBgpLayer, payload, payloadLen);
9191
}
9292

9393
std::string BgpLayer::toString() const

Packet++/src/IPv6Layer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ namespace pcpp
288288
}
289289
case PACKETPP_IPPROTO_ICMPV6:
290290
{
291-
setNextLayer(IcmpV6Layer::parseIcmpV6Layer(payload, payloadLen, this, getAttachedPacket()));
291+
constructNextLayerFromFactory(IcmpV6Layer::parseIcmpV6Layer, payload, payloadLen);
292292
break;
293293
}
294294
case PACKETPP_IPPROTO_VRRP:

Packet++/src/LLCLayer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace pcpp
3535

3636
if (hdr->dsap == 0x42 && hdr->ssap == 0x42 && StpLayer::isDataValid(payload, payloadLen))
3737
{
38-
setNextLayer(StpLayer::parseStpLayer(payload, payloadLen, this, getAttachedPacket()));
38+
constructNextLayerFromFactory(StpLayer::parseStpLayer, payload, payloadLen);
3939
}
4040

4141
if (!hasNextLayer())

Packet++/src/LdapLayer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ namespace pcpp
372372
uint8_t* payload = m_Data + headerLen;
373373
size_t payloadLen = m_DataLen - headerLen;
374374

375-
setNextLayer(LdapLayer::parseLdapMessage(payload, payloadLen, this, getAttachedPacket()));
375+
constructNextLayerFromFactory(LdapLayer::parseLdapMessage, payload, payloadLen);
376376
}
377377
// endregion
378378

Packet++/src/SSHLayer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ namespace pcpp
3333
size_t headerLen = getHeaderLen();
3434
if (m_DataLen <= headerLen)
3535
return;
36-
setNextLayer(SSHLayer::createSSHMessage(m_Data + headerLen, m_DataLen - headerLen, this, getAttachedPacket()));
36+
37+
constructNextLayerFromFactory(SSHLayer::createSSHMessage, m_Data + headerLen, m_DataLen - headerLen);
3738
}
3839

3940
// --------------------------------

Packet++/src/SSLLayer.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ namespace pcpp
9595
return;
9696

9797
if (SSLLayer::IsSSLMessage(0, 0, m_Data + headerLen, m_DataLen - headerLen, true))
98-
setNextLayer(
99-
SSLLayer::createSSLMessage(m_Data + headerLen, m_DataLen - headerLen, this, getAttachedPacket()));
98+
{
99+
constructNextLayerFromFactory(SSLLayer::createSSLMessage, m_Data + headerLen, m_DataLen - headerLen);
100+
}
100101
}
101102

102103
// -------------------------

Packet++/src/SomeIpLayer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ namespace pcpp
264264
uint8_t* payload = m_Data + headerLen;
265265
size_t payloadLen = m_DataLen - headerLen;
266266

267-
setNextLayer(parseSomeIpLayer(payload, payloadLen, this, getAttachedPacket()));
267+
constructNextLayerFromFactory(parseSomeIpLayer, payload, payloadLen);
268268
}
269269

270270
std::string SomeIpLayer::toString() const

Packet++/src/TcpLayer.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ namespace pcpp
381381
}
382382
else if (SSLLayer::IsSSLMessage(portSrc, portDst, payload, payloadLen))
383383
{
384-
setNextLayer(SSLLayer::createSSLMessage(payload, payloadLen, this, getAttachedPacket()));
384+
constructNextLayerFromFactory(SSLLayer::createSSLMessage, payload, payloadLen);
385385
}
386386
else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc))
387387
{
@@ -401,13 +401,11 @@ namespace pcpp
401401
}
402402
else if (BgpLayer::isBgpPort(portSrc, portDst))
403403
{
404-
m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, getAttachedPacket());
405-
if (!m_NextLayer)
406-
constructNextLayer<PayloadLayer>(payload, payloadLen, getAttachedPacket());
404+
tryConstructNextLayerFromFactoryWithFallback<PayloadLayer>(BgpLayer::parseBgpLayer, payload, payloadLen);
407405
}
408406
else if (SSHLayer::isSSHPort(portSrc, portDst))
409407
{
410-
setNextLayer(SSHLayer::createSSHMessage(payload, payloadLen, this, getAttachedPacket()));
408+
constructNextLayerFromFactory(SSHLayer::createSSHMessage, payload, payloadLen);
411409
}
412410
else if (DnsLayer::isDataValid(payload, payloadLen, true) &&
413411
(DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc)))
@@ -434,13 +432,11 @@ namespace pcpp
434432
else if ((DoIpLayer::isDoIpPort(portSrc) || DoIpLayer::isDoIpPort(portDst)) &&
435433
(DoIpLayer::isDataValid(payload, payloadLen)))
436434
{
437-
m_NextLayer = DoIpLayer::parseDoIpLayer(payload, payloadLen, this, getAttachedPacket());
438-
if (!m_NextLayer)
439-
constructNextLayer<PayloadLayer>(payload, payloadLen, getAttachedPacket());
435+
tryConstructNextLayerFromFactoryWithFallback<PayloadLayer>(DoIpLayer::parseDoIpLayer, payload, payloadLen);
440436
}
441437
else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst))
442438
{
443-
setNextLayer(SomeIpLayer::parseSomeIpLayer(payload, payloadLen, this, getAttachedPacket()));
439+
constructNextLayerFromFactory(SomeIpLayer::parseSomeIpLayer, payload, payloadLen);
444440
}
445441
else if (TpktLayer::isDataValid(payload, payloadLen) && TpktLayer::isTpktPort(portSrc, portDst))
446442
{
@@ -456,9 +452,8 @@ namespace pcpp
456452
}
457453
else if (LdapLayer::isLdapPort(portDst) || LdapLayer::isLdapPort(portSrc))
458454
{
459-
m_NextLayer = LdapLayer::parseLdapMessage(payload, payloadLen, this, getAttachedPacket());
460-
if (!m_NextLayer)
461-
constructNextLayer<PayloadLayer>(payload, payloadLen, getAttachedPacket());
455+
tryConstructNextLayerFromFactoryWithFallback<PayloadLayer>(LdapLayer::parseLdapMessage, payload,
456+
payloadLen);
462457
}
463458
else if ((GtpV2Layer::isGTPv2Port(portDst) || GtpV2Layer::isGTPv2Port(portSrc)) &&
464459
GtpV2Layer::isDataValid(payload, payloadLen))

Packet++/src/UdpLayer.cpp

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,9 @@ namespace pcpp
116116
}
117117
else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc))
118118
{
119-
setNextLayer(SipLayer::parseSipLayer(udpData, udpDataLen, this, getAttachedPacket(), portSrc, portDst));
120-
if (!hasNextLayer())
121-
{
122-
constructNextLayer<PayloadLayer>(udpData, udpDataLen, getAttachedPacket());
123-
}
119+
// Resolves the overload of parseSipLayer, without static_casting a function pointer.
120+
auto* (*fac)(uint8_t*, size_t, Layer*, Packet*, uint16_t, uint16_t) = SipLayer::parseSipLayer;
121+
tryConstructNextLayerFromFactoryWithFallback<PayloadLayer>(fac, udpData, udpDataLen, portSrc, portDst);
124122
}
125123
else if ((RadiusLayer::isRadiusPort(portDst) || RadiusLayer::isRadiusPort(portSrc)) &&
126124
RadiusLayer::isDataValid(udpData, udpDataLen))
@@ -150,14 +148,11 @@ namespace pcpp
150148
else if ((DoIpLayer::isDoIpPort(portSrc) || DoIpLayer::isDoIpPort(portDst)) &&
151149
(DoIpLayer::isDataValid(udpData, udpDataLen)))
152150
{
153-
setNextLayer(DoIpLayer::parseDoIpLayer(udpData, udpDataLen, this, getAttachedPacket()));
154-
if (!hasNextLayer())
155-
constructNextLayer<PayloadLayer>(udpData, udpDataLen);
151+
tryConstructNextLayerFromFactoryWithFallback<PayloadLayer>(DoIpLayer::parseDoIpLayer, udpData, udpDataLen);
156152
}
157153
else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst))
158154
{
159-
160-
setNextLayer(SomeIpLayer::parseSomeIpLayer(udpData, udpDataLen, this, getAttachedPacket()));
155+
constructNextLayerFromFactory(SomeIpLayer::parseSomeIpLayer, udpData, udpDataLen);
161156
}
162157
else if ((WakeOnLanLayer::isWakeOnLanPort(portDst) && WakeOnLanLayer::isDataValid(udpData, udpDataLen)))
163158
{
@@ -166,9 +161,8 @@ namespace pcpp
166161
else if ((WireGuardLayer::isWireGuardPorts(portDst, portSrc) &&
167162
WireGuardLayer::isDataValid(udpData, udpDataLen)))
168163
{
169-
setNextLayer(WireGuardLayer::parseWireGuardLayer(udpData, udpDataLen, this, getAttachedPacket()));
170-
if (!hasNextLayer())
171-
constructNextLayer<PayloadLayer>(udpData, udpDataLen);
164+
tryConstructNextLayerFromFactoryWithFallback<PayloadLayer>(WireGuardLayer::parseWireGuardLayer, udpData,
165+
udpDataLen);
172166
}
173167

174168
// If a valid layer was found, return immediately
@@ -178,7 +172,11 @@ namespace pcpp
178172
}
179173

180174
// Here, heuristics for all protocols should be invoked to determine the correct layer
181-
setNextLayer(SipLayer::parseSipLayer(udpData, udpDataLen, this, getAttachedPacket()));
175+
{
176+
// Resolves the overload of parseSipLayer, without static_casting a function pointer.
177+
auto* (*fac)(uint8_t*, size_t, Layer*, Packet*) = SipLayer::parseSipLayer;
178+
tryConstructNextLayerFromFactoryWithFallback<PayloadLayer>(fac, udpData, udpDataLen);
179+
}
182180

183181
if (!hasNextLayer())
184182
{

0 commit comments

Comments
 (0)