|Just another Perl shrine|
Net::Ping, the mini seriesby tye (Cardinal)
|on Nov 21, 2000 at 22:00 UTC||Need Help??|
TCP and UDP pings vs. ICMP pings
Okay, TCP and UDP "pings" used to be supported on "most" computers. For me, the story starts with a typical Unix system that had a inetd process (InterNET Daemon) that would read a configuration file that gave it a fairly long list of TCP and UDP ports to listen() on and what program to run when it received a request on which port. This was nice because, for example, you could handle FTP requests without having to have ftpd sitting around in your process table all day when you rarely used it (though that isn't an efficient configuration if you are constantly servicing a lot of FTP requests).
Well inetd would default to listening to TCP port 7 and UDP port 7. When a request came in on either of those ports it would echo any data received back to client (and the service name for port 7 is "echo").
Now Windows boxes don't come with inetd. You can get an inetd (I think it is either on a resource kit or can be downloaded or something) and configure it to respond to port 7 requests. But that requires way too much work for most people to get something that doesn't do them any good (it does other people good).
Also, the rising awareness of denial of service attacks has lead to many people turning off port 7 support in their inetd configuration files (sometimes due to identified weaknesses, sometimes just to "be safe" since there was little use for these services).
Meanwhile, many ICMP packets need to be handled by the TCP/IP stack itself [for example, so it can return "host unreachable" to a TCP connect() request if an ICMP packet comes in saying that the host is unreachable]. So the TCP/IP stack also handles ICMP echo requests (pings) itself without the need for inetd nor its configuration file.
So all (or nearly all) computers will respond to ICMP echo requests. Some firewalls get configured to block these, but other than that, an ICMP echo request is a very reliable type of "ping". Meanwhile, the proportion of computers that will process TCP or UDP echo requests is quite low and probably falling.
TCP pings could be great
A TCP ping could actually be about as useful as an ICMP ping if the Net::Ping module did them correctly.
I used Net::Ping on a Unix box to perform a TCP ping to a computer that I knew was up. It turns out that the other computer didn't support TCP echo requests. Well, Net::Ping reported that the ping failed. That was stupid.
I used the Perl debugger to step into Net::Ping and found that the connect() was failing with "Connection refused". What does this mean? It means that the other computer is up, is reachable, and isn't listening on TCP port 7 (and it did this all without triggering the time out). Well, that is a wonderful form of a successful ping in my book. When I ping a computer, I don't want to be told whether it is listening on port 7! So Net::Ping should return a true value for this case. Instead it returns a false value and manages to hide the value of $! so you can't just change the false to true if $! is "Connection refused". *sigh*
There are two (other) important practical differences between TCP and ICMP pings, however. First, either can be blocked at a firewall, rendering it useless. Why is that a difference? Well, if the firewalls you pass through block ICMP pings but not TCP echos, then that is a big difference. I suspect that TCP pings are commonly blocked at firewalls because there is no reason for them and a secure site should block anything that isn't required to be unblocked. I suspect that ICMP pings are often blocked to prevent probing of your inner network by strangers. So I suspect that TCP pings will have at least slightly more problems with firewalls.
Second, TCP pings require a time out or a nonblocking connect(). Net::Ping requires a time out for TCP pings, which means that you can't use Net::Ping to do TCP pings under Win32 [since Win32 Perl doesn't support the alarm() call]. You could patch Net::Ping to use nonblocking connect() instead, but that wouldn't do you any good because the Win32 nonblocking connect() is only nonblocking when you use the nonstandard Async* APIs. It is supposed to work using the standard nonblocking socket option. However, that option makes everything but connect() nonblocking. This is just a bug in Win32's winsock library. *sigh*
UDP pings don't get "Connection refused"
Now UDP pings don't require a time out [and don't use connect()]. But they don't get "Connection refused" so they can't detect machines that are up but not listening to UDP port 7. Actually, I think that you could detect the "Packet refused" condition [or the other errors like "host unreachable" which a TCP connect() will detect immediately and save you from waiting for a time out]. But doing that requires catching and parsing ICMP packets. Well, if you can do that then you might as well just do ICMP pings. *sigh*
Non-administrators can't use ICMP under WinNT/Win2K
Here is a quote from Microsoft documentation concerning the creation of "raw" sockets for processing TCP/IP packets other than TCP or UDP.
Note: On Windows NT/Windows 2000, raw socket support requires administrative privileges. Users running Winsock applications that make use of raw sockets must have administrative privileges on the computer, otherwise raw socket calls fail with an error code of WSAEACCESS.So you can use ICMP pings under Win9x (which I have verified) [since there is no such thing as a Win9x administrator]. Under WinNT/Win2K, you can't open a "raw" socket to get full access to all kinds of packets unless you are an administrator.
So how does PING.EXE do it?
Ping uses ICMP.DLL
I popped up the "Depends" tool [no, it doesn't catch "Adult accidents"] from MS Developers' Studio and examined PING.EXE. I quickly discovered that it uses three functions from ICMP.DLL:
Here is a paraphrase of the documentation for IcmpSendEcho():
I'll try to put something together to use these in the next few days.- tye (but my friends call me "Tye")