25

I'm writing a local network scanner on Windows to find online hosts with IP Helper Functions, which is equivalent to nmap -PR but without WinPcap. I know SendARP will block and send arp request 3 times if the remote host doesn't respond, so I use std::aync to create one threads for each host, but the problem is I want to send an ARP request every 20ms so it would not be too much arp packets in a very short time.

#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;
}

At first I can do this by calling Sleep(20) after launching a thread, but once SendARP in these threads re-send ARP requests if no replies from remote host, it's out of my control, and I see many requests in a very short time(<10ms) in Wireshark, so my question is:

  1. Any way to make SendARP asynchronous?
  2. if not, can I control the sent timing of SendARP in threads?
jfly
  • 7,715
  • 3
  • 35
  • 65
  • 1
    It seems more pragmatic to simply limit the number of unresolved ARP requests. i.e. start by sending 5 requests, as you get replies, send more. – Chris Becke Apr 26 '17 at 08:48
  • I want a quick ARP sweep(in 5s) of local network, for a typical `/24` network, scanning the whole network with sending a ARP request every 20ms costs about 5s, but `SendARP` may block for 3s if remote host down on Windows 7/8/10. – jfly Apr 26 '17 at 09:02
  • 3
    I'm missing the problem. "Many requests in a very short time" suggests the rate is too high, yet "I want a quick sweep" suggests the rate is too low. What is it? You'll need 254 threads since you can't have two blocking calls lasting 3 seconds each within that 5 second interval. All 254 calls must be started within the first two seconds. – MSalters Apr 26 '17 at 09:14
  • Given you can't control the internals of SendArp's timeout and retransmit... sending your own arp requests using raw sockets might be the only option... with the drawback that the app must run as administrator... – Chris Becke Apr 26 '17 at 09:17
  • That's exactly what I wanna do - control the rate, send a request every 20ms. if `SendARP` send only one ARP request, I can launch a thread then sleep 20ms, but it doesn't. – jfly Apr 26 '17 at 09:19
  • @ChrisBecke administrator privilege is acceptable, but Windows raw socket doesn't allow sending layer 2 packets. I know I can do this with WinPcap, but then it depends on npf driver. – jfly Apr 26 '17 at 09:25
  • @jfly really? This answer on linux indicates linux raw sockets can http://stackoverflow.com/questions/16710040/arp-request-and-reply-using-c-socket-programming and I thought the raw socket impls were largely similar at the details they expose. (Well, other than openBSD/OSX that won't let you sniff tcp or udp traffic). I see other responses that do indicate that the headers needed for arp are not exposed by windows raw sockets. libpcap then. – Chris Becke Apr 26 '17 at 13:05
  • This feature does not make any sense. What could possibly change on the machine that it drops an arp packet on the floor but 20 milliseconds later it doesn't? Operating systems do not behave that way. Only sensible approach is to send arp packets to *different* machines, but that makes timing completely immaterial. The OS feature you are looking for does not exist because they could not think of a reason why anybody would need it. You don't need it. – Hans Passant Apr 28 '17 at 06:16
  • @HansPassant Thanks for your reply, but in my another test program implemented with winpcap, it shows sending request every 20ms can get more ARP responses than sending all right away. I've tested it in 1000+ different networks, the strategy with rate control wins nearly all cases. So I want this without winpcap. – jfly Apr 28 '17 at 06:30
  • Well, of course you'll get more responses, purely from the target machine not being able to respond in less than 20 msec. You need only the first one. Count the number of *effective* responses by omitting the duplicates. – Hans Passant Apr 28 '17 at 06:51
  • Yeah, I mean the strategy found more live hosts, no duplicates, both run for 5s. – jfly Apr 28 '17 at 07:05
  • As was mentioned by @Chris Becke, you're really looking for raw sockets here (that is craft your own packets and tell the OS to forward it without adding headers). [this link](http://www.binarytides.com/raw-sockets-using-winsock/) seems to suggest that Windows has periodically removed and added support for raw sockets throughout the years and may have settled on removing support all together. The TLDR for your problem here is either use Linux, or use WinPcap. You can checkout [this link](https://msdn.microsoft.com/en-us/library/windows/desktop/ms740548(v=vs.85).aspx) for more info – Howard May 03 '17 at 10:49

1 Answers1

0

There doesn't seem to be any way to force SendARP to act in a non-blocking manner, it would appear that when a host is unreachable, it will try to re-query several times before giving up.

As for the solution, nothing you want to hear. the MSDN Docs state that there's a newer API that deprecates SendARP called ResolveIpNetEntry2 that can also do the same thing, but it also appears to behave in the same manner.

The struct it receives contains a field called ReachabilityTime.LastUnreachable which is: The time, in milliseconds, that a node assumes a neighbor is unreachable after not having received a reachability confirmation.

However, it does not appear to have any real effect.

The best way to do it, is to use WinPCap or some other driver, there doesn't seem to be a way of solving your problem in userland.

A. Smoliak
  • 438
  • 2
  • 17