Skip to content

Commit f370aa1

Browse files
committed
- Split classes into separate files for better understandability and maintenance
- Add automatic network interface discovery and monitoring - Implement dynamic capture thread management for interface changes - Add sleep/wake recovery and new connection detection
1 parent 33934b4 commit f370aa1

File tree

8 files changed

+874
-529
lines changed

8 files changed

+874
-529
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
whatpulse-pcap-service

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CC=g++
22
CFLAGS=-std=c++17 -Wall -Wextra -O2 -pthread
33
LDFLAGS=-lpcap -pthread
44
TARGET=whatpulse-pcap-service
5-
SOURCES=main.cpp pcapservice.cpp
5+
SOURCES=main.cpp pcapservice.cpp tcpclient.cpp pcapcapturethread.cpp
66
VERSION=1.0.0
77

88
# Default target

pcapcapturethread.cpp

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* WhatPulse External PCap Service - PCap Capture Thread Implementation
3+
*
4+
* Copyright (c) 2025 WhatPulse. All rights reserved.
5+
*
6+
* Licensed under CC BY-NC 4.0 with additional terms.
7+
* See LICENSE file for complete terms and conditions.
8+
*
9+
* NOTICE: This software integrates with WhatPulse services. Reverse engineering
10+
* the communication protocol or tampering with data transmission is prohibited.
11+
*
12+
* For licensing questions: [email protected]
13+
*/
14+
15+
#include "pcapcapturethread.h"
16+
#include "pcapservice.h"
17+
#include <iostream>
18+
#include <cstring>
19+
#include <chrono>
20+
#include <net/ethernet.h>
21+
#include <netinet/ip.h>
22+
#include <netinet/ip6.h>
23+
#include <unistd.h>
24+
25+
// Protocol constants
26+
#define ETHERTYPE_IP 0x0800
27+
#define ETHERTYPE_IPV6 0x86dd
28+
29+
// Performance optimization constants (matching built-in PCap monitor)
30+
#define DEFAULT_SNAPLEN 9000
31+
32+
PcapCaptureThread::PcapCaptureThread(const std::string &interface, bool verbose, PcapService *service)
33+
{
34+
m_interface = interface;
35+
m_verbose = verbose;
36+
m_capturing = false;
37+
m_shouldStop = false;
38+
m_pcapHandle = nullptr;
39+
m_service = service;
40+
}
41+
42+
PcapCaptureThread::~PcapCaptureThread()
43+
{
44+
stop();
45+
join();
46+
}
47+
48+
void PcapCaptureThread::start()
49+
{
50+
m_thread = std::make_unique<std::thread>(&PcapCaptureThread::run, this);
51+
}
52+
53+
void PcapCaptureThread::stop()
54+
{
55+
m_shouldStop.store(true);
56+
57+
std::lock_guard<std::mutex> lock(m_mutex);
58+
if (m_pcapHandle)
59+
{
60+
pcap_breakloop(m_pcapHandle);
61+
}
62+
}
63+
64+
void PcapCaptureThread::join()
65+
{
66+
if (m_thread && m_thread->joinable())
67+
{
68+
m_thread->join();
69+
}
70+
}
71+
72+
void PcapCaptureThread::run()
73+
{
74+
char errbuf[PCAP_ERRBUF_SIZE];
75+
76+
if (m_verbose)
77+
{
78+
std::cout << "Starting capture on interface: " << m_interface << std::endl;
79+
}
80+
81+
// Open pcap handle
82+
m_pcapHandle = pcap_open_live(m_interface.c_str(),
83+
DEFAULT_SNAPLEN, // optimized snap length
84+
1, // promiscuous mode
85+
1, // timeout (ms) - reduced for better performance
86+
errbuf);
87+
88+
if (!m_pcapHandle)
89+
{
90+
std::cerr << "Unable to open interface " << m_interface << ": " << errbuf << std::endl;
91+
return;
92+
}
93+
94+
// Set filter to capture only TCP and UDP traffic
95+
struct bpf_program filter;
96+
if (pcap_compile(m_pcapHandle, &filter, "tcp or udp", 1, PCAP_NETMASK_UNKNOWN) == -1)
97+
{
98+
std::cerr << "Unable to compile filter for interface " << m_interface << ": "
99+
<< pcap_geterr(m_pcapHandle) << std::endl;
100+
pcap_close(m_pcapHandle);
101+
m_pcapHandle = nullptr;
102+
return;
103+
}
104+
105+
if (pcap_setfilter(m_pcapHandle, &filter) == -1)
106+
{
107+
std::cerr << "Unable to set filter for interface " << m_interface << ": "
108+
<< pcap_geterr(m_pcapHandle) << std::endl;
109+
pcap_freecode(&filter);
110+
pcap_close(m_pcapHandle);
111+
m_pcapHandle = nullptr;
112+
return;
113+
}
114+
115+
pcap_freecode(&filter);
116+
m_capturing.store(true);
117+
118+
if (m_verbose)
119+
{
120+
std::cout << "Capture started successfully on interface: " << m_interface << std::endl;
121+
}
122+
123+
// Start packet capture loop
124+
while (!m_shouldStop.load())
125+
{
126+
int result = pcap_dispatch(m_pcapHandle, 1000, packetHandler, reinterpret_cast<u_char *>(this));
127+
if (result == -1)
128+
{
129+
// Error occurred
130+
if (!m_shouldStop.load())
131+
{
132+
std::cerr << "Error in pcap_dispatch for interface " << m_interface << ": "
133+
<< pcap_geterr(m_pcapHandle) << std::endl;
134+
}
135+
break;
136+
}
137+
else if (result == -2)
138+
{
139+
// Loop was broken
140+
break;
141+
}
142+
143+
// No sleep needed with larger batch size - let it run at full speed
144+
}
145+
146+
m_capturing.store(false);
147+
148+
{
149+
std::lock_guard<std::mutex> lock(m_mutex);
150+
if (m_pcapHandle)
151+
{
152+
pcap_close(m_pcapHandle);
153+
m_pcapHandle = nullptr;
154+
}
155+
}
156+
157+
if (m_verbose)
158+
{
159+
std::cout << "Capture stopped on interface: " << m_interface << std::endl;
160+
}
161+
}
162+
163+
void PcapCaptureThread::packetHandler(u_char *userData, const struct pcap_pkthdr *header, const u_char *packet)
164+
{
165+
PcapCaptureThread *thread = reinterpret_cast<PcapCaptureThread *>(userData);
166+
thread->handlePacket(header, packet);
167+
}
168+
169+
void PcapCaptureThread::handlePacket(const struct pcap_pkthdr *header, const u_char *packet)
170+
{
171+
if (m_shouldStop.load())
172+
{
173+
return;
174+
}
175+
176+
// Parse Ethernet header
177+
const struct ether_header *ethHeader = reinterpret_cast<const struct ether_header *>(packet);
178+
179+
uint16_t etherType = ntohs(ethHeader->ether_type);
180+
uint8_t ipVersion = 0;
181+
182+
if (etherType == ETHERTYPE_IP)
183+
{
184+
ipVersion = 4;
185+
}
186+
else if (etherType == ETHERTYPE_IPV6)
187+
{
188+
ipVersion = 6;
189+
}
190+
else
191+
{
192+
// Not IP traffic, ignore
193+
return;
194+
}
195+
196+
// Create packet data structure
197+
PacketData packetData;
198+
packetData.ipVersion = ipVersion;
199+
packetData.dataLength = header->caplen;
200+
packetData.timestamp = static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(
201+
std::chrono::system_clock::now().time_since_epoch())
202+
.count());
203+
packetData.interfaceName = m_interface;
204+
205+
// Copy packet data starting from IP header (skip Ethernet header)
206+
const u_char *ipPacket = packet + sizeof(struct ether_header);
207+
uint32_t ipPacketLength = header->caplen - sizeof(struct ether_header);
208+
209+
packetData.packetData.assign(ipPacket, ipPacket + ipPacketLength);
210+
211+
// Send to service
212+
if (m_service)
213+
{
214+
m_service->onPacketCaptured(packetData);
215+
}
216+
}

pcapcapturethread.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* WhatPulse External PCap Service - PCap Capture Thread
3+
*
4+
* Copyright (c) 2025 WhatPulse. All rights reserved.
5+
*
6+
* Licensed under CC BY-NC 4.0 with additional terms.
7+
* See LICENSE file for complete terms and conditions.
8+
*
9+
* NOTICE: This software integrates with WhatPulse services. Reverse engineering
10+
* the communication protocol or tampering with data transmission is prohibited.
11+
*
12+
* For licensing questions: [email protected]
13+
*/
14+
15+
#ifndef PCAPCAPTURETHREAD_H
16+
#define PCAPCAPTURETHREAD_H
17+
18+
#include <string>
19+
#include <vector>
20+
#include <memory>
21+
#include <thread>
22+
#include <mutex>
23+
#include <atomic>
24+
#include <cstdint>
25+
#include <pcap/pcap.h>
26+
27+
// Forward declarations
28+
class PcapService;
29+
30+
/**
31+
* Structure for packet data transmission
32+
*/
33+
struct PacketData
34+
{
35+
uint8_t ipVersion; // 4 or 6
36+
uint16_t dataLength; // Packet length
37+
uint32_t timestamp; // Packet timestamp
38+
std::vector<uint8_t> packetData; // Raw packet data (IP header onwards)
39+
std::string interfaceName; // Interface name where packet was captured
40+
41+
PacketData() : ipVersion(0), dataLength(0), timestamp(0) {}
42+
};
43+
44+
/**
45+
* Thread class for capturing packets on a specific interface
46+
*/
47+
class PcapCaptureThread
48+
{
49+
public:
50+
explicit PcapCaptureThread(const std::string &interface, bool verbose, PcapService *service);
51+
~PcapCaptureThread();
52+
53+
void start();
54+
void stop();
55+
void join();
56+
bool isCapturing() const { return m_capturing.load(); }
57+
const std::string &interfaceName() const { return m_interface; }
58+
59+
private:
60+
void run();
61+
static void packetHandler(u_char *userData, const struct pcap_pkthdr *header, const u_char *packet);
62+
void handlePacket(const struct pcap_pkthdr *header, const u_char *packet);
63+
64+
std::string m_interface;
65+
bool m_verbose;
66+
std::atomic<bool> m_capturing;
67+
std::atomic<bool> m_shouldStop;
68+
69+
pcap_t *m_pcapHandle;
70+
std::unique_ptr<std::thread> m_thread;
71+
PcapService *m_service;
72+
73+
mutable std::mutex m_mutex;
74+
};
75+
76+
#endif // PCAPCAPTURETHREAD_H

0 commit comments

Comments
 (0)