http://qs321.pair.com?node_id=42739

Okay my first reply on this subject was a fairly quick one to get Ovid some useful information in the little time I had. Here are some more details (and the results of some research).

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:

  • HANDLE IcmpCreateFile( void );
  • BOOL IcmpCloseHandle( HANDLE IcmpHandle );
  • DWORD IcmpSendEcho(
    • HANDLE IcmpHandle
    • IPAddr DestinationAddress
    • void * RequestData
    • WORD RequestSize
    • PIP_OPTION_INFORMATION RequestOptions
    • void * ReplyBuffer
    • DWORD ReplySize
    • DWORD Timeout
    );
So you could use the Inline module [or, if you don't have a C compiler and can get someone to compile C::Dynalib, FFI, or Win32::API for you, then use one of those] to use these functions to perform ICMP pings under WinNT/Win2K without administrator rights.

Here is a paraphrase of the documentation for IcmpSendEcho():

It sends an ICMP echo request and returns one or more replies.

See Icmpapi.h. Uses ICMP.DLL.

IcmpHandle is returned by IcmpCreateFile() [duh].

DestinationAddress is the obvious. I haven't found the documentation on the structure of IPAddr just yet.

RequestData is the data to be included in the request (can be used to test for certain packet sizes, problematic data values, and/or to line up requests with responses).

RequestSize is the number of bytes in RequestData.

RequestOptions is NULL or points to the IP header options for the request. I haven't found the documentation on this struct either yet.

ReplyBuffer is the buffer for any replies. The buffer will contain one or more ICMP_ECHO_REPLY structures, followed by options and data.

ReplySize is the size (in bytes) of ReplyBuffer and must be at least 8+sizeof(ICMP_ECHO_REPLY) [8 is the size of an ICMP error message].

Timeout is the time in milliseconds.

The return value is the number of replies received and stored in the reply buffer or zero for failure. See $^E for failure reason.

I'll try to put something together to use these in the next few days.

        - tye (but my friends call me "Tye")

Replies are listed 'Best First'.
Re: Net::Ping the mini series (UDP Pings)
by rdw (Curate) on Nov 22, 2000 at 14:27 UTC

    Excellent stuff tye. I just thought I'd something which might help.

    You can use connect with UDP sockets - its just an API thing though and it sets the 'default address' to which packets are sent, as well as filtering packets to only be received from that address. That's not very useful, but what is more useful is that you can detect the 'port unreachable' ICMP messages that would be sent back through the normal API. The error ICMP packets which the ip stack receives are associated with the socket and so if you try and send another packet after one of these error packets has been received, you get an error back from the send (although the packet is still sent I think). So, if you know that the host isn't listening on a particular UDP port, then a simple UDP ping can be done without needing any special permissions.

    Anyway, try this - it works for me!

    #! /usr/bin/perl -w use strict; use IO::Socket::INET; my($host, $port) = qw(localhost 12345); my $sock = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port, Proto => 'udp') || die; my $retry = 6; while ($retry--) { unless (send($sock, "ping!", 0)) { print "Host is up? ($!)\n"; } sleep(1); }

    Have fun,

    rdw

      it doesn't work for me :( or precisely it's works always: if udp port is up or down ! i need to test for my web servers some ports, I have only problems to test the UDP ones... How can i do that ? Thanks for support sachab@usa.net
        I know this is an old post, but I think I might know why your program in saying 'yes' all the time.

        The reason it might be always reporting that the server is up might be that if it is a linux box, iptables might have icmp host unreachable turned off. If that's the case then when your send call is done, it returns without error and in the gap between the nth and the (n+1)th send call, the OS reports an ICMP host unreachable, but due to the iptables rule, it gets dropped at the point of entry, never making it to the program. So your subsequent sends do not see any errors reported by the previous ones and it looks as if all is well.

Re: Net::Ping, the mini series
by Macphisto (Hermit) on Nov 21, 2000 at 22:16 UTC
    Really, my only comment is that this is classic tye. That boy never seems to disappoint when it comes to posts. Especially in depth posts that have to be written well due to the subject matter. Very informative post, if I could ++ you more than once, I would...but until that day comes, I'll just add more to my tye shrine...:)

    Macphisto

    Everyone has their demons....
(redmist) Re: Net::Ping, the mini series
by redmist (Deacon) on Nov 21, 2000 at 23:10 UTC
    That's some Grade A posting action tye! Thanks for teaching me something new.

    IDENTIFICATION DIVISION.
    PROGRAM-ID.  redmist.
    AUTHOR.  God (Larry Wall/Alan Cox hybrid).
    CONTACT.  redmist@users.sourceforge.net
    
Re: Net::Ping, the mini series
by meredith (Friar) on Jun 06, 2003 at 12:22 UTC
    Just to chime in, since this is an old post: The year is now 2003, and Win32 Perl (5.8.0 at writing) does support the alarm() call, hence alleviating one issue with using Net::Ping. But, unfortunately, security has become even more important, and you're unlikely to find any systems that allow TCP or UDP pings. I'd suggest doing your 'ping' by connecting to another known port, for example 25/smtp. After connecting you can issue the command HELO, which the server will reply with a hello back, then issue a QUIT before closing. This is also a good check of higher-level function on the remote system, but that's another node! ;)

    mhoward - at - hattmoward.org