Programmatically using ping (IcmpSendcho2 and IcmpParseReplies)

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



The "blessed" way to send ping messages, and the only nonprivileged way to
do it in Vista, is to use a variant of IcmpSendcho.
Unfortunately, you can run into a lot of trouble with these functions. They
are poorly documented.
Because of that, you might have a program crash, or your app might not
register a valid reply even if it did get one.
Or your pogram won't link.

The biggest problem is the documentation.
Some important features in the help reference are poorly phrased and some
important warnings are missing.
Also some functions are specified wrong in the SDK header files.

The problems can all be overcome but it's a long story.
Here are my comments on the documentation of IcmpSendcho2, found on
http://msdn.microsoft.com/en-us/library/aa366051(VS.85).aspx

1. The reply buffer (ReplyBuffer)

Warning: When you use IcmpSendcho2 asynchronously, with IcmpParseReplies,
this buffer must exist until the ping attempt is completed.
If you de-allocate the buffer before you get your reply, or before your
timeout is expired, your program will crash in Vista.
Do _not_ close the ICMP handle or deallocate your buffer, until after your
timeout.

From what I could make of it, it looks like your ping attempt is complete:
- after you get a reply
- after calling IcmpParseReplies when the timeout period has expired.

The documentation does not specify when it is safe to delete your buffer. =>
clarification needed.

I think that the whole construct would be a lot safer if we could have an
equivalent of CancleIo.

2. The size of the reply buffer (ReplySize)

A quote from msdn.microsoft.com:
"ReplyBuffer [out]
A pointer to a buffer to hold any replies to the request. Upon return, the
buffer contains an array of ICMP_ECHO_REPLY structures followed by options
and data. The buffer must be large enough to hold at least one
ICMP_ECHO_REPLY structure plus RequestSize bytes of data. On a 64-bit
platform, upon return the buffer contains an array of ICMP_ECHO_REPLY32
structures followed by the options and data for the replies.

This buffer should also be large enough to also hold 8 more bytes of data
(the size of an ICMP error message) plus space for an IO_STATUS_BLOCK
structure."

Problem #1: I am doing application software, how am I supposed to know the
size of an IO_STATUS_BLOCK?! This is kernel stuff - I don't want to include
kernel headers.

Problem #2: "8 more bytes of data" 8 more than what? Suppose I knew the
size of IO_STATUS_BLOCK, it could mean either:

n * sizeof (ICMP_ECHO_REPLY) + __max(MY_OWN_REQUESTDATA_SIZE,
IO_STATUS_BLOCK_SIZE + 8)
or:
n * (sizeof (ICMP_ECHO_REPLY) + __max(MY_OWN_REQUESTDATA_SIZE,
IO_STATUS_BLOCK_SIZE) + 8)
or:
sizeof (ICMP_ECHO_REPLY) + MY_OWN_REQUESTDATA_SIZE + IO_STATUS_BLOCK_SIZE +
8
[or many other things]

I went on to experiment a bit, using this for a buffer size:

sizeof(ICMP_ECHO_REPLY) + MY_OWN_PAYLOAD_SIZE, PING_MIN_EXTRA_SIZE)

where PING_MIN_EXTRA_SIZE was a constant that I tried with different values.

For values less than 12, my asynchronous ping would fail with
ERROR_INSUFFICIENT_BUFFER.
For values from 12 to 19, the ping would appear to succeed, but a subsequent
IcmpParseReplies would fail to parse a valid reply.
For values of 20 and more the ping behaves normally.

Buffer size => a proper and simple formula is needed, using an explicit
number of bytes, not an obscure kernel structure.

3. Timeout value.

For asynchronous use of IcmpSendEcho2, the documentation states
" [..] this parameter is not used if either the ApcRoutine or Event
parameter are not NULL."

Wrong!

For Vista, you _do_ need to set a timeout.
If you only follow the documentation, use an event handle, and set the
timeout to 0, your response will never arrive and you _always_ get a
timeout.
Use an appropriate timeout value instead, and make sure that you do not
delete your buffers until the timeout has expired.

4. IcmpParseReplies in icmpapi.h from the SDK does not match the import
library Iphlpapi.lib. You get a link failure.
(Or at least, the "Microsoft Windows Software Development Kit for Windows
Vista Update (6000.16384.10)" has this problem.)

In order to link your application, you need to do the following:
- Locate icmpapi.h
- Locate the function IcmpParseReplies.
It should look something like this:

DWORD
IcmpParseReplies(
LPVOID ReplyBuffer,
DWORD ReplySize
);

Add the keyword WINAPI, like this:

DWORD
WINAPI
IcmpParseReplies(
LPVOID ReplyBuffer,
DWORD ReplySize
);

This changes the calling convention to __stdcall, the variant offered by the
import library.
You will find similar comments in the online version of the MSDN
documentation.


.



Relevant Pages

  • Re: Programmatically using ping (IcmpSendcho2 and IcmpParseReplies)
    ... The biggest problem is the documentation. ... When you use IcmpSendcho2 asynchronously, with IcmpParseReplies, ... this buffer must exist until the ping attempt is completed. ... timeout is expired, your program will crash in Vista. ...
    (microsoft.public.win32.programmer.networks)
  • Re: bpf/pcap are weird
    ... If non-blocking reads *do* cause buffer rotation if necessary, this can, ... timeout in their select loop, be worked around by making the timeout ...
    (freebsd-arch)
  • Re: Some questions on writing data to a serial port?
    ... int serial_write ... while (nbytes < count) { ... O_NONBLOCK) to implement a timeout. ... buffer cache, which is flushed to the server asynchronously. ...
    (comp.unix.programmer)
  • Re: setvbuf accross exec
    ... buffer, e.g. a pty. ... if an application is flushing each buffer? ... that the output isn't being flushed if the timeout isn't ... There is no timeout value for a flush. ...
    (comp.unix.programmer)
  • Re: ral0 interface hangs with the message "No buffer space available"
    ... > ping 192.168.0.1 ... ping: sendto: No buffer space available ... I don't believe that TCP buffer is legitimately filled up to the max ...
    (freebsd-hackers)