#define LOG_MODULE NetworkUtils
#include <condition_variable>
#include <errno.h>
#include <mutex>
#include <stdlib.h>
#include "Logger.h"
#include "Packet.h"
#include "EthLayer.h"
#include "ArpLayer.h"
#include "IPv4Layer.h"
#include "UdpLayer.h"
#include "DnsLayer.h"
#include "PcapFilter.h"
#include "NetworkUtils.h"
#include "EndianPortable.h"
#ifdef _MSC_VER
#include "SystemUtils.h"
#endif
#ifndef ETIMEDOUT
#define ETIMEDOUT 10060
#endif
#define DNS_PORT 53
namespace pcpp
{
const int NetworkUtils::DefaultTimeout = 5;
struct ArpingReceivedData
{
std::mutex &mutex;
std::condition_variable &cond;
IPv4Address ipAddr;
clock_t start;
MacAddress result;
double arpResponseTime;
};
static void arpPacketReceived(RawPacket* rawPacket, PcapLiveDevice*, void* userCookie)
{
clock_t receiveTime = clock();
ArpingReceivedData* data = (ArpingReceivedData*)userCookie;
Packet packet(rawPacket);
if (!packet.isPacketOfType(ARP))
return;
ArpLayer* arpReplyLayer = packet.getLayerOfType<ArpLayer>(true); if (arpReplyLayer == nullptr)
return;
if (arpReplyLayer->getArpHeader()->hardwareType != htobe16(1)
|| arpReplyLayer->getArpHeader()->protocolType != htobe16(PCPP_ETHERTYPE_IP))
return;
if (arpReplyLayer->getSenderIpAddr() != data->ipAddr)
return;
double diffticks = receiveTime-data->start;
double diffms = (diffticks*1000)/CLOCKS_PER_SEC;
data->arpResponseTime = diffms;
data->result = arpReplyLayer->getSenderMacAddress();
data->cond.notify_one();
}
MacAddress NetworkUtils::getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, double& arpResponseTimeMS,
MacAddress sourceMac, IPv4Address sourceIP, int arpTimeout) const
{
MacAddress result = MacAddress::Zero;
bool closeDeviceAtTheEnd = false;
if (!device->isOpened())
{
closeDeviceAtTheEnd = true;
if (!device->open())
{
PCPP_LOG_ERROR("Cannot open device");
return result;
}
}
if (sourceMac == MacAddress::Zero)
sourceMac = device->getMacAddress();
if (sourceIP == IPv4Address::Zero)
sourceIP = device->getIPv4Address();
if (arpTimeout <= 0)
arpTimeout = NetworkUtils::DefaultTimeout;
Packet arpRequest(100);
MacAddress destMac(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
EthLayer ethLayer(sourceMac, destMac);
ArpLayer arpLayer(ARP_REQUEST, sourceMac, destMac, sourceIP, ipAddr);
if (!arpRequest.addLayer(ðLayer))
{
PCPP_LOG_ERROR("Couldn't build Eth layer for ARP request");
return result;
}
if (!arpRequest.addLayer(&arpLayer))
{
PCPP_LOG_ERROR("Couldn't build ARP layer for ARP request");
return result;
}
arpRequest.computeCalculateFields();
ArpFilter arpFilter(ARP_REPLY);
if (!device->setFilter(arpFilter))
{
PCPP_LOG_ERROR("Couldn't set ARP filter for device");
return result;
}
std::mutex mutex;
std::condition_variable cond;
ArpingReceivedData data = {
mutex,
cond,
ipAddr,
clock(),
MacAddress::Zero,
0
};
struct timeval now;
gettimeofday(&now,nullptr);
device->startCapture(arpPacketReceived, &data);
device->sendPacket(&arpRequest);
std::unique_lock<std::mutex> lock(mutex);
std::cv_status res = cond.wait_for(lock, std::chrono::seconds(arpTimeout));
device->stopCapture();
if (res == std::cv_status::timeout)
{
PCPP_LOG_ERROR("ARP request time out");
return result;
}
if (closeDeviceAtTheEnd)
device->close();
else
device->clearFilter();
result = data.result;
arpResponseTimeMS = data.arpResponseTime;
return result;
}
struct DNSReceivedData
{
std::mutex &mutex;
std::condition_variable &cond;
std::string hostname;
uint16_t transactionID;
clock_t start;
IPv4Address result;
uint32_t ttl;
double dnsResponseTime;
};
static void dnsResponseReceived(RawPacket* rawPacket, PcapLiveDevice*, void* userCookie)
{
clock_t receiveTime = clock();
DNSReceivedData* data = (DNSReceivedData*)userCookie;
Packet packet(rawPacket);
if (!packet.isPacketOfType(DNS))
return;
DnsLayer* dnsResponseLayer = packet.getLayerOfType<DnsLayer>(true); if (dnsResponseLayer == nullptr)
return;
if (dnsResponseLayer->getDnsHeader()->queryOrResponse != 1
|| dnsResponseLayer->getDnsHeader()->numberOfAnswers < htobe16(1)
|| dnsResponseLayer->getDnsHeader()->transactionID != htobe16(data->transactionID))
{
return;
}
std::string hostToFind = data->hostname;
DnsResource* dnsAnswer = nullptr;
while (true)
{
dnsAnswer = dnsResponseLayer->getAnswer(hostToFind, true);
if (dnsAnswer == nullptr)
{
PCPP_LOG_DEBUG("DNS answer doesn't contain hostname '" << hostToFind << "'");
return;
}
DnsType dnsType = dnsAnswer->getDnsType();
if (dnsType == DNS_TYPE_A)
{
PCPP_LOG_DEBUG("Found IPv4 resolving for hostname '" << hostToFind << "'");
break;
}
else if (dnsType == DNS_TYPE_CNAME)
{
PCPP_LOG_DEBUG("Got a DNS response for hostname '" << hostToFind << "' with CNAME '" << dnsAnswer->getData()->toString() << "'");
hostToFind = dnsAnswer->getData()->toString();
}
else
{
PCPP_LOG_DEBUG("Got a DNS response with type which is not A or CNAME");
return;
}
}
clock_t diffticks = receiveTime-data->start;
double diffms = (diffticks*1000.0)/CLOCKS_PER_SEC;
data->dnsResponseTime = diffms;
data->result = dnsAnswer->getData()->castAs<IPv4DnsResourceData>()->getIpAddress();
data->ttl = dnsAnswer->getTTL();
data->cond.notify_one();
}
IPv4Address NetworkUtils::getIPv4Address(const std::string& hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL,
int dnsTimeout, IPv4Address dnsServerIP, IPv4Address gatewayIP) const
{
IPv4Address result = IPv4Address::Zero;
bool closeDeviceAtTheEnd = false;
if (!device->isOpened())
{
closeDeviceAtTheEnd = true;
if (!device->open())
{
PCPP_LOG_ERROR("Cannot open device");
return result;
}
}
if (gatewayIP == IPv4Address::Zero)
{
gatewayIP = device->getDefaultGateway();
}
if (!gatewayIP.isValid() || gatewayIP == IPv4Address::Zero)
{
PCPP_LOG_ERROR("Gateway address isn't valid or couldn't find default gateway");
return result;
}
double arpResTime;
MacAddress gatewayMacAddress = getMacAddress(gatewayIP, device, arpResTime);
if (gatewayMacAddress == MacAddress::Zero)
{
PCPP_LOG_ERROR("Couldn't resolve gateway MAC address");
return result;
}
if (dnsTimeout <= 0)
dnsTimeout = NetworkUtils::DefaultTimeout;
if (dnsServerIP == IPv4Address::Zero && device->getDnsServers().size() > 0)
{
dnsServerIP = device->getDnsServers().at(0);
}
if (!dnsServerIP.isValid())
{
PCPP_LOG_ERROR("DNS server IP isn't valid");
return result;
}
Packet dnsRequest(100);
MacAddress sourceMac = device->getMacAddress();
EthLayer ethLayer(sourceMac, gatewayMacAddress, PCPP_ETHERTYPE_IP);
IPv4Layer ipLayer(device->getIPv4Address(), dnsServerIP);
ipLayer.getIPv4Header()->timeToLive = 128;
int srcPortLowest = 10000;
int srcPortRange = 65535 - srcPortLowest;
uint16_t srcPort = (rand() % srcPortRange) + srcPortLowest;
UdpLayer udpLayer(srcPort, DNS_PORT);
DnsLayer dnsLayer;
uint16_t transactionID = rand() % 65535;
dnsLayer.getDnsHeader()->transactionID = htobe16(transactionID);
dnsLayer.addQuery(hostname, DNS_TYPE_A, DNS_CLASS_IN);
if (!dnsRequest.addLayer(ðLayer) || !dnsRequest.addLayer(&ipLayer) || !dnsRequest.addLayer(&udpLayer) || !dnsRequest.addLayer(&dnsLayer))
{
PCPP_LOG_ERROR("Couldn't construct DNS query");
return result;
}
dnsRequest.computeCalculateFields();
PortFilter dnsResponseFilter(53, SRC);
if (!device->setFilter(dnsResponseFilter))
{
PCPP_LOG_ERROR("Couldn't set DNS response filter");
return result;
}
std::mutex mutex;
std::condition_variable cond;
DNSReceivedData data = {
mutex,
cond,
hostname,
transactionID,
clock(),
IPv4Address::Zero,
0,
0
};
struct timeval now;
gettimeofday(&now,nullptr);
device->startCapture(dnsResponseReceived, &data);
device->sendPacket(&dnsRequest);
std::unique_lock<std::mutex> lock(mutex);
std::cv_status res = cond.wait_for(lock, std::chrono::seconds(dnsTimeout));
device->stopCapture();
if (res == std::cv_status::timeout)
{
PCPP_LOG_ERROR("DNS request time out");
return result;
}
if (closeDeviceAtTheEnd)
device->close();
else
device->clearFilter();
result = data.result;
dnsResponseTimeMS = data.dnsResponseTime;
dnsTTL = data.ttl;
return result;
}
}