#include "NetworkDetector.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <regex>
#include <cstring>
#ifdef _WIN32
#include <winsock2.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <unistd.h>
#endif
NetworkDetector::NetworkDetector(double packetLossThreshold, int latencyThreshold)
: packetLossThreshold_(packetLossThreshold), latencyThreshold_(latencyThreshold) {
}
bool NetworkDetector::detect() {
std::vector<NetworkInterface> interfaces = getAllInterfaces();
bool networkIssueDetected = false;
for (const auto& interface : interfaces) {
if (interface.isUp) {
std::cout << "Testing network interface: " << interface.name << " (" << interface.ipAddress << ")" << std::endl;
std::vector<std::string> testTargets = {"8.8.8.8", "1.1.1.1", "208.67.222.222"};
for (const auto& target : testTargets) {
double packetLoss;
int latency;
if (pingTest(target, packetLoss, latency)) {
std::cout << " Ping to " << target << ": "
<< "Packet loss: " << packetLoss << "%, "
<< "Latency: " << latency << "ms" << std::endl;
if (packetLoss > packetLossThreshold_ || latency > latencyThreshold_) {
std::cout << " Network issue detected on " << interface.name
<< " when connecting to " << target << std::endl;
networkIssueDetected = true;
}
} else {
std::cout << " Failed to ping " << target << " from " << interface.name << std::endl;
networkIssueDetected = true;
}
}
}
}
return networkIssueDetected;
}
std::vector<NetworkDetector::NetworkInterface> NetworkDetector::getAllInterfaces() {
std::vector<NetworkInterface> interfaces;
#ifdef _WIN32
ULONG bufferSize = 15000;
PIP_ADAPTER_ADDRESSES pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(bufferSize);
if (pAddresses == nullptr) {
std::cerr << "Memory allocation failed for IP_ADAPTER_ADDRESSES struct" << std::endl;
return interfaces;
}
DWORD result = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &bufferSize);
if (result == ERROR_BUFFER_OVERFLOW) {
free(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(bufferSize);
if (pAddresses == nullptr) {
std::cerr << "Memory allocation failed for IP_ADAPTER_ADDRESSES struct" << std::endl;
return interfaces;
}
result = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &bufferSize);
}
if (result == NO_ERROR) {
PIP_ADAPTER_ADDRESSES pCurrent = pAddresses;
while (pCurrent) {
if (pCurrent->OperStatus == IfOperStatusUp) {
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pCurrent->FirstUnicastAddress;
while (pUnicast) {
if (pUnicast->Address.lpSockaddr->sa_family == AF_INET) {
NetworkInterface interface;
char name[256];
WideCharToMultiByte(CP_ACP, 0, pCurrent->FriendlyName, -1, name, sizeof(name), nullptr, nullptr);
interface.name = name;
sockaddr_in* sockaddr = reinterpret_cast<sockaddr_in*>(pUnicast->Address.lpSockaddr);
char ipStr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(sockaddr->sin_addr), ipStr, INET_ADDRSTRLEN);
interface.ipAddress = ipStr;
interface.isUp = true;
interface.packetLossRate = 0.0;
interface.latency = 0;
interfaces.push_back(interface);
}
pUnicast = pUnicast->Next;
}
}
pCurrent = pCurrent->Next;
}
} else {
std::cerr << "GetAdaptersAddresses failed with error code: " << result << std::endl;
}
if (pAddresses) {
free(pAddresses);
}
#else
struct ifaddrs* ifaddr;
if (getifaddrs(&ifaddr) == -1) {
std::cerr << "getifaddrs failed: " << strerror(errno) << std::endl;
return interfaces;
}
for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) {
NetworkInterface interface;
interface.name = ifa->ifa_name;
char ipStr[INET_ADDRSTRLEN];
struct sockaddr_in* addr = (struct sockaddr_in*)ifa->ifa_addr;
inet_ntop(AF_INET, &addr->sin_addr, ipStr, INET_ADDRSTRLEN);
interface.ipAddress = ipStr;
interface.isUp = (ifa->ifa_flags & IFF_UP) && (ifa->ifa_flags & IFF_RUNNING);
interface.packetLossRate = 0.0;
interface.latency = 0;
interfaces.push_back(interface);
}
}
freeifaddrs(ifaddr);
#endif
return interfaces;
}
NetworkDetector::NetworkInterface NetworkDetector::testInterface(const std::string& interfaceName) {
auto interfaces = getAllInterfaces();
for (auto& interface : interfaces) {
if (interface.name == interfaceName) {
std::vector<std::string> testTargets = {"8.8.8.8", "1.1.1.1"};
double totalPacketLoss = 0.0;
int totalLatency = 0;
int successfulTests = 0;
for (const auto& target : testTargets) {
double packetLoss;
int latency;
if (pingTest(target, packetLoss, latency)) {
totalPacketLoss += packetLoss;
totalLatency += latency;
successfulTests++;
}
}
if (successfulTests > 0) {
interface.packetLossRate = totalPacketLoss / successfulTests;
interface.latency = totalLatency / successfulTests;
} else {
interface.packetLossRate = 100.0;
interface.latency = 0;
}
return interface;
}
}
NetworkInterface emptyInterface;
emptyInterface.name = interfaceName;
emptyInterface.isUp = false;
emptyInterface.packetLossRate = 0.0;
emptyInterface.latency = 0;
return emptyInterface;
}
bool NetworkDetector::pingTest(const std::string& target, double& packetLoss, int& latency) {
std::string pingCmd;
std::string outputFile = "ping_output.txt";
#ifdef _WIN32
pingCmd = "ping -n 5 " + target + " > " + outputFile + " 2>&1";
#else
pingCmd = "ping -c 5 " + target + " > " + outputFile + " 2>&1";
#endif
int result = std::system(pingCmd.c_str());
if (result != 0) {
packetLoss = 100.0;
latency = 0;
return false;
}
std::ifstream file(outputFile);
if (!file.is_open()) {
std::cerr << "Failed to open ping output file" << std::endl;
packetLoss = 100.0;
latency = 0;
return false;
}
std::string line;
packetLoss = 100.0;
latency = 0;
#ifdef _WIN32
std::regex lossPattern("\\(([0-9]+)% loss\\)");
std::regex latencyPattern("Average = ([0-9]+)ms");
while (std::getline(file, line)) {
std::smatch match;
if (std::regex_search(line, match, lossPattern) && match.size() > 1) {
packetLoss = std::stod(match[1].str());
}
if (std::regex_search(line, match, latencyPattern) && match.size() > 1) {
latency = std::stoi(match[1].str());
}
}
#else
std::regex lossPattern("([0-9\\.]+)% packet loss");
std::regex latencyPattern("rtt min/avg/max/mdev = [0-9\\.]+/([0-9\\.]+)/[0-9\\.]+/[0-9\\.]+ ms");
while (std::getline(file, line)) {
std::smatch match;
if (std::regex_search(line, match, lossPattern) && match.size() > 1) {
packetLoss = std::stod(match[1].str());
}
if (std::regex_search(line, match, latencyPattern) && match.size() > 1) {
latency = std::stoi(match[1].str());
}
}
#endif
file.close();
std::remove(outputFile.c_str());
return true;
}
void NetworkDetector::setPacketLossThreshold(double threshold) {
packetLossThreshold_ = threshold;
}
void NetworkDetector::setLatencyThreshold(int threshold) {
latencyThreshold_ = threshold;
}