我正在 Windows 上编写一个本地网络扫描仪,用于查找具有 IP Helper Functions 的在线主机,这相当于
nmap -PR
,但没有 WinPcap。我知道如果远程主机没有响应,SendARP会阻塞并发送 3 次 arp 请求,所以我使用 std::aync
为每个主机创建一个线程,但问题是我想每 20ms 发送一个 ARP 请求,所以短时间内不会有太多的arp数据包。
#include <iostream>
#include <future>
#include <vector>
#include <winsock2.h>
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main(int argc, char **argv)
{
ULONG MacAddr[2]; /* for 6-byte hardware addresses */
ULONG PhysAddrLen = 6; /* default to length of six bytes */
memset(&MacAddr, 0xff, sizeof (MacAddr));
PhysAddrLen = 6;
IPAddr SrcIp = 0;
IPAddr DestIp = 0;
char buf[64] = {0};
size_t start = time(NULL);
std::vector<std::future<DWORD> > vResults;
for (auto i = 1; i< 255; i++)
{
sprintf(buf, "192.168.1.%d", i);
DestIp = inet_addr(buf);
vResults.push_back(std::async(std::launch::async, std::ref(SendARP), DestIp, SrcIp, MacAddr, &PhysAddrLen));
Sleep(20);
}
for (auto it= vResults.begin(); it != vResults.end(); ++it)
{
if (it->get() == NO_ERROR)
{
std::cout<<"host up\n";
}
}
std::cout<<"time elapsed "<<(time(NULL) - start)<<std::endl;
return 0;
}
起初我可以通过在启动线程后调用
Sleep(20)
来做到这一点,但是一旦这些线程中的SendARP
重新发送ARP请求,如果没有来自远程主机的回复,它就超出了我的控制范围,并且我在一个线程中看到了很多请求。时间很短(<10ms) in Wireshark, so my question is:
SendARP
异步吗?SendARP
的发送时间吗?似乎没有任何方法可以强制
SendARP
以非阻塞方式行事,看起来当主机无法访问时,它会尝试重新查询几次然后放弃。
至于解决方案,没有什么是你想听到的。 MSDN 文档指出,有一个较新的 API 弃用了
SendARP
,称为 ResolveIpNetEntry2,它也可以执行相同的操作,但它的行为方式似乎也相同。
它接收到的结构体包含一个名为
ReachabilityTime.LastUnreachable
的字段,它是: 节点在未收到可达性确认后假定邻居不可达的时间(以毫秒为单位)。
然而,似乎并没有任何实际效果。
最好的方法是使用 WinPCap 或其他一些驱动程序,似乎没有办法解决用户空间中的问题。
不幸的是,Windows中的SendARP函数没有提供用于异步执行或控制ARP请求时序的内置机制。它是一个阻塞功能,发送 ARP 请求并等待响应或超时,如果没有回复,您无法控制重新发送 ARP 请求的时机。
为了实现以受控时序异步发送 ARP 请求的目标,您需要使用原始套接字或较低级别的网络库来实现自己的 ARP 请求逻辑。这将使您能够更好地控制 ARP 请求的时间和并发性。
以下是您可以执行的操作的高级概述:
使用原始套接字或 libpcap/winpcap 等网络库发送 ARP 请求。 为每个要向其发送 ARP 请求的主机创建一个单独的线程。 在每个线程中,发送 ARP 请求并等待响应(您可以设置自己的超时时间)。 使用每个线程中的睡眠或其他机制控制 ARP 请求的时间。
以下是如何在单独的线程中使用原始套接字发送 ARP 请求的示例:
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
#include <winsock2.h>
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
using namespace std;
void sendArpRequest(const char* ipAddress) {
// Create a raw socket and send ARP request here.
// You'll need to implement the ARP packet structure and send/receive logic.
// Use Sleep or another mechanism to control the timing.
}
int main() {
vector<thread> threads;
for (int i = 1; i < 255; i++) {
char ipAddress[20];
sprintf(ipAddress, "192.168.1.%d", i);
threads.emplace_back(sendArpRequest, ipAddress);
this_thread::sleep_for(chrono::milliseconds(20)); // Control the timing
}
for (auto& thread : threads) {
thread.join();
}
return 0;
}
使用原始套接字实现 ARP 请求可能很复杂,并且您需要在代码中处理 ARP 响应和超时。您可能还需要以提升的权限运行程序来创建原始套接字。此外,请确保您的代码中有适当的错误处理和资源管理。