@@ -286,6 +286,14 @@ namespace pcpp
286286 RawPacketVector* capturedPackets = nullptr ;
287287 };
288288
289+ struct CaptureContextWithCancellation
290+ {
291+ PcapLiveDevice* device = nullptr ;
292+ OnPacketArrivesStopBlocking callback;
293+ void * userCookie = nullptr ;
294+ bool requestStop = false ;
295+ };
296+
289297 // A noop function to be used when no callback is set
290298 void onPacketArrivesNoop (uint8_t * user, const pcap_pkthdr* pkthdr, const uint8_t * packet)
291299 {}
@@ -310,6 +318,46 @@ namespace pcpp
310318 context->callback (&rawPacket, context->device , context->userCookie );
311319 }
312320
321+ // @brief Wraps the raw packet data into a RawPacket instance and calls the user callback with stop indication
322+ void onPacketArrivesCallbackWithCancellation (uint8_t * user, const pcap_pkthdr* pkthdr, const uint8_t * packet)
323+ {
324+ auto * context = reinterpret_cast <CaptureContextWithCancellation*>(user);
325+ if (context == nullptr || context->device == nullptr || context->callback == nullptr )
326+ {
327+ PCPP_LOG_ERROR (" Unable to extract PcapLiveDevice instance or callback" );
328+ return ;
329+ }
330+
331+ if (context->requestStop )
332+ {
333+ // If requestStop is true, there is no need to process the packet
334+ PCPP_LOG_DEBUG (" Capture request stop is set, skipping packet processing" );
335+ return ;
336+ }
337+
338+ RawPacket rawPacket (packet, pkthdr->caplen , pkthdr->ts , false , context->device ->getLinkType ());
339+
340+ try
341+ {
342+ if (context->callback (&rawPacket, context->device , context->userCookie ))
343+ {
344+ // If the callback returns true, it means that the user wants to stop the capture
345+ PCPP_LOG_DEBUG (" Capture callback requested to stop capturing" );
346+ context->requestStop = true ;
347+ }
348+ }
349+ catch (const std::exception& ex)
350+ {
351+ PCPP_LOG_ERROR (" Exception occurred while invoking packet arrival callback: " << ex.what ());
352+ context->requestStop = true ; // Stop capture on exception
353+ }
354+ catch (...)
355+ {
356+ PCPP_LOG_ERROR (" Unknown exception occurred while invoking packet arrival callback" );
357+ context->requestStop = true ; // Stop capture on unknown exception
358+ }
359+ }
360+
313361 // / @brief Wraps the raw packet data into a RawPacket instance and adds it to the captured packets vector
314362 // / @param user A pointer to an AccumulatorCaptureContext instance
315363 // / @param pkthdr A pointer to the pcap_pkthdr struct
@@ -429,33 +477,13 @@ namespace pcpp
429477 m_CaptureThreadStarted = false ;
430478 m_StopThread = false ;
431479 m_CaptureThread = {};
432- m_cbOnPacketArrivesBlockingMode = nullptr ;
433- m_cbOnPacketArrivesBlockingModeUserCookie = nullptr ;
434480 if (calculateMacAddress)
435481 {
436482 setDeviceMacAddress ();
437483 PCPP_LOG_DEBUG (" MAC addr: " << m_MacAddress);
438484 }
439485 }
440486
441- void PcapLiveDevice::onPacketArrivesBlockingMode (uint8_t * user, const struct pcap_pkthdr * pkthdr,
442- const uint8_t * packet)
443- {
444- PcapLiveDevice* pThis = reinterpret_cast <PcapLiveDevice*>(user);
445- if (pThis == nullptr )
446- {
447- PCPP_LOG_ERROR (" Unable to extract PcapLiveDevice instance" );
448- return ;
449- }
450-
451- RawPacket rawPacket (packet, pkthdr->caplen , pkthdr->ts , false , pThis->getLinkType ());
452-
453- if (pThis->m_cbOnPacketArrivesBlockingMode != nullptr )
454- if (pThis->m_cbOnPacketArrivesBlockingMode (&rawPacket, pThis,
455- pThis->m_cbOnPacketArrivesBlockingModeUserCookie ))
456- pThis->m_StopThread = true ;
457- }
458-
459487 internal::PcapHandle PcapLiveDevice::doOpen (const DeviceConfiguration& config)
460488 {
461489 char errbuf[PCAP_ERRBUF_SIZE] = { ' \0 ' };
@@ -813,14 +841,15 @@ namespace pcpp
813841 return 0 ;
814842 }
815843
816- m_cbOnPacketArrivesBlockingMode = std::move (onPacketArrives);
817- m_cbOnPacketArrivesBlockingModeUserCookie = userCookie;
818-
819844 m_CaptureThreadStarted = true ;
820845 m_StopThread = false ;
821846
822- const int64_t timeoutMs = timeout * 1000 ; // timeout unit is seconds, let's change it to milliseconds
847+ // A valid timeout is only generated when timeout is positive.
848+ // This means that the timeout timepoint should be after the start time.
849+ const bool hasTimeout = timeout > 0 ;
823850 auto startTime = std::chrono::steady_clock::now ();
851+ // Calculate the timeout timepoint, cast the double timeout (in seconds) to milliseconds for greater precision
852+ auto timeoutTime = startTime + std::chrono::milliseconds (static_cast <int64_t >(timeout * 1000 ));
824853 auto currentTime = startTime;
825854
826855#if !defined(_WIN32)
@@ -832,30 +861,40 @@ namespace pcpp
832861
833862 bool shouldReturnError = false ;
834863
835- if (timeoutMs <= 0 )
864+ CaptureContextWithCancellation context;
865+ context.device = this ;
866+ context.callback = std::move (onPacketArrives);
867+ context.userCookie = userCookie;
868+ context.requestStop = false ;
869+
870+ // No timeout specified, run until stopped
871+ if (!hasTimeout)
836872 {
837873 while (!m_StopThread)
838874 {
839- if (pcap_dispatch (m_PcapDescriptor.get (), -1 , onPacketArrivesBlockingMode ,
840- reinterpret_cast <uint8_t *>(this )) == -1 )
875+ if (pcap_dispatch (m_PcapDescriptor.get (), -1 , onPacketArrivesCallbackWithCancellation ,
876+ reinterpret_cast <uint8_t *>(&context )) == -1 )
841877 {
842878 PCPP_LOG_ERROR (" pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError ());
843879 shouldReturnError = true ;
844880 m_StopThread = true ;
845881 }
882+ else if (context.requestStop )
883+ {
884+ // If the callback requested to stop the capture, we break the loop
885+ m_StopThread = true ;
886+ }
846887 }
847888 }
848889 else
849890 {
850- auto const timeoutTimepoint = startTime + std::chrono::milliseconds (timeoutMs);
851-
852- while (!m_StopThread && currentTime < timeoutTimepoint)
891+ while (!m_StopThread && currentTime < timeoutTime)
853892 {
854893 if (m_UsePoll)
855894 {
856895#if !defined(_WIN32)
857896 int64_t pollTimeoutMs =
858- std::chrono::duration_cast<std::chrono::milliseconds>(timeoutTimepoint - currentTime).count ();
897+ std::chrono::duration_cast<std::chrono::milliseconds>(timeoutTime - currentTime).count ();
859898
860899 // poll will be in blocking mode if negative value
861900 pollTimeoutMs = std::max (pollTimeoutMs, static_cast <int64_t >(0 ));
@@ -864,13 +903,18 @@ namespace pcpp
864903
865904 if (ready > 0 )
866905 {
867- if (pcap_dispatch (m_PcapDescriptor.get (), -1 , onPacketArrivesBlockingMode ,
868- reinterpret_cast <uint8_t *>(this )) == -1 )
906+ if (pcap_dispatch (m_PcapDescriptor.get (), -1 , onPacketArrivesCallbackWithCancellation ,
907+ reinterpret_cast <uint8_t *>(&context )) == -1 )
869908 {
870909 PCPP_LOG_ERROR (" pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError ());
871910 shouldReturnError = true ;
872911 m_StopThread = true ;
873912 }
913+ else if (context.requestStop )
914+ {
915+ // If the callback requested to stop the capture, we break the loop
916+ m_StopThread = true ;
917+ }
874918 }
875919 else if (ready < 0 )
876920 {
@@ -886,40 +930,46 @@ namespace pcpp
886930 }
887931 else
888932 {
889- if (pcap_dispatch (m_PcapDescriptor.get (), -1 , onPacketArrivesBlockingMode ,
890- reinterpret_cast <uint8_t *>(this )) == -1 )
933+ if (pcap_dispatch (m_PcapDescriptor.get (), -1 , onPacketArrivesCallbackWithCancellation ,
934+ reinterpret_cast <uint8_t *>(&context )) == -1 )
891935 {
892936 PCPP_LOG_ERROR (" pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError ());
893937 shouldReturnError = true ;
894938 m_StopThread = true ;
895939 }
940+ else if (context.requestStop )
941+ {
942+ // If the callback requested to stop the capture, we break the loop
943+ m_StopThread = true ;
944+ }
896945 }
897946 currentTime = std::chrono::steady_clock::now ();
898947 }
899948 }
900949
901950 m_CaptureThreadStarted = false ;
902951 m_StopThread = false ;
903- m_cbOnPacketArrivesBlockingMode = nullptr ;
904- m_cbOnPacketArrivesBlockingModeUserCookie = nullptr ;
905952
906953 if (shouldReturnError)
907954 {
908955 return 0 ;
909956 }
910957
911- if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count () >= timeoutMs)
958+ // Check the time only if a valid timeout was specified. Otherwise it would always be true.
959+ if (hasTimeout && currentTime >= timeoutTime)
912960 {
913- return -1 ;
961+ return -1 ; // If we are past the timeout time, return -1
914962 }
915963 return 1 ;
916964 }
917965
918966 void PcapLiveDevice::stopCapture ()
919967 {
920- // in blocking mode stop capture isn't relevant
921- if (m_cbOnPacketArrivesBlockingMode != nullptr )
968+ // In blocking mode, there is no capture thread, so we don't need to stop it
969+ if (!m_CaptureThread.joinable ())
970+ {
922971 return ;
972+ }
923973
924974 if (m_CaptureThread.get_id () != std::thread::id{} && m_CaptureThread.get_id () == std::this_thread::get_id ())
925975 {
0 commit comments