3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS ping utility
5 * FILE: apps/net/ping/ping.c
6 * PURPOSE: Network test utility
7 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
9 * CSH 01/09/2000 Created
11 //#include <windows.h>
21 /* FIXME: Where should this be? */
25 #define CopyMemory(Destination, Source, Length) memcpy(Destination, Source, Length);
27 /* Should be in the header files somewhere (exported by ntdll.dll) */
28 long atol(const char *str
);
31 typedef long long __int64
;
34 char * _i64toa(__int64 value
, char *string
, int radix
);
39 /* General ICMP constants */
40 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
41 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
43 /* ICMP message types */
44 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
45 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
49 /* IPv4 header structure */
50 typedef struct _IPv4_HEADER
{
52 unsigned char Version
:4;
54 unsigned short Length
;
56 unsigned short FragFlags
;
58 unsigned char Protocol
;
59 unsigned short Checksum
;
60 unsigned int SrcAddress
;
61 unsigned int DstAddress
;
62 } IPv4_HEADER
, *PIPv4_HEADER
;
64 /* ICMP echo request/reply header structure */
65 typedef struct _ICMP_HEADER
{
68 unsigned short Checksum
;
70 unsigned short SeqNum
;
71 } ICMP_HEADER
, *PICMP_HEADER
;
73 typedef struct _ICMP_ECHO_PACKET
{
75 LARGE_INTEGER Timestamp
;
76 } ICMP_ECHO_PACKET
, *PICMP_ECHO_PACKET
;
82 BOOL ResolveAddresses
;
84 UINT DataSize
; /* ICMP echo request data size */
99 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
100 LARGE_INTEGER MaxRTT
;
101 LARGE_INTEGER SumRTT
;
102 LARGE_INTEGER AvgRTT
;
103 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
104 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
105 BOOL UsePerformanceCounter
;
108 /* Display the contents of a buffer */
116 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
119 for (i
= 0; i
< Size
; i
++) {
123 printf("%02X ", (p
[i
]) & 0xFF);
127 /* Display usage information on screen */
130 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
131 printf("Options:\n");
132 printf(" -t Ping the specified host until stopped.\n");
133 printf(" To stop - type Control-C.\n");
134 printf(" -n count Number of echo requests to send.\n");
135 printf(" -l size Send buffer size.\n");
136 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
139 /* Reset configuration to default values */
142 LARGE_INTEGER PerformanceCounterFrequency
;
145 ResolveAddresses
= FALSE
;
148 DontFragment
= FALSE
;
152 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
154 if (UsePerformanceCounter
) {
155 /* Performance counters may return incorrect results on some multiprocessor
156 platforms so we restrict execution on the first processor. This may fail
157 on Windows NT so we fall back to GetCurrentTick() for timing */
158 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0) {
159 UsePerformanceCounter
= FALSE
;
162 /* Convert frequency to ticks per millisecond */
163 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
164 /* And to ticks per microsecond */
165 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
167 if (!UsePerformanceCounter
) {
168 /* 1 tick per millisecond for GetCurrentTick() */
169 TicksPerMs
.QuadPart
= 1;
170 /* GetCurrentTick() cannot handle microseconds */
171 TicksPerUs
.QuadPart
= 1;
175 /* Return ULONG in a string */
176 ULONG
GetULONG(LPSTR String
)
182 Length
= strlen(String
);
183 while ((i
< Length
) && ((String
[i
] < '0') || (String
[i
] > '9'))) i
++;
184 if ((i
>= Length
) || ((String
[i
] < '0') || (String
[i
] > '9'))) {
185 InvalidOption
= TRUE
;
188 Value
= (ULONG
)atol(&String
[i
]);
193 /* Return ULONG in a string. Try next paramter if not successful */
194 ULONG
GetULONG2(LPSTR String1
, LPSTR String2
, PINT i
)
198 Value
= GetULONG(String1
);
200 InvalidOption
= FALSE
;
201 if (String2
[0] != '-') {
202 Value
= GetULONG(String2
);
211 /* Parse command line parameters */
212 BOOL
ParseCmdline(int argc
, char* argv
[])
218 // lstrcpy(TargetName, "127.0.0.1");
228 InvalidOption
= FALSE
;
230 for (i
= 1; i
< argc
; i
++) {
231 if (argv
[i
][0] == '-') {
232 switch (argv
[i
][1]) {
233 case 't': NeverStop
= TRUE
; break;
234 case 'a': ResolveAddresses
= TRUE
; break;
235 case 'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
237 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
238 if ((DataSize
< 0) || (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
))) {
239 printf("Bad value for option -l, valid range is from 0 to %d.\n",
240 ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
));
244 case 'f': DontFragment
= TRUE
; break;
245 case 'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
246 case 'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
247 case 'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
249 printf("Bad option %s.\n", argv
[i
]);
254 printf("Bad option format %s.\n", argv
[i
]);
259 printf("Bad parameter %s.\n", argv
[i
]);
262 lstrcpy(TargetName
, argv
[i
]);
268 if ((!ShowUsage
) && (!FoundTarget
)) {
269 printf("Name or IP address of destination host must be specified.\n");
280 /* Calculate checksum of data */
281 WORD
Checksum(PUSHORT data
, UINT size
)
287 size
-= sizeof(USHORT
);
291 sum
+= *(UCHAR
*)data
;
293 sum
= (sum
>> 16) + (sum
& 0xFFFF);
296 return (USHORT
)(~sum
);
299 /* Prepare to ping target */
302 WORD wVersionRequested
;
308 wVersionRequested
= MAKEWORD(2, 2);
310 Status
= WSAStartup(wVersionRequested
, &WsaData
);
312 printf("Could not initialize winsock dll.\n");
316 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
317 if (IcmpSock
== INVALID_SOCKET
) {
318 printf("Could not create socket (#%d).\n", WSAGetLastError());
322 ZeroMemory(&Target
, sizeof(Target
));
324 Addr
= inet_addr(TargetName
);
325 if (Addr
== INADDR_NONE
) {
326 phe
= gethostbyname(TargetName
);
328 printf("Unknown host %s.\n", TargetName
);
334 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
336 Target
.sin_addr
.s_addr
= Addr
;
340 Target
.sin_family
= phe
->h_addrtype
;
342 Target
.sin_family
= AF_INET
;
345 TargetIP
= inet_ntoa(Target
.sin_addr
);
359 if (IcmpSock
!= INVALID_SOCKET
)
360 closesocket(IcmpSock
);
365 VOID
QueryTime(PLARGE_INTEGER Time
)
367 if (UsePerformanceCounter
) {
368 if (QueryPerformanceCounter(Time
) == 0) {
369 /* This should not happen, but we fall
370 back to GetCurrentTick() if it does */
371 Time
->u
.LowPart
= (ULONG
)GetTickCount();
372 Time
->u
.HighPart
= 0;
374 /* 1 tick per millisecond for GetCurrentTick() */
375 TicksPerMs
.QuadPart
= 1;
376 /* GetCurrentTick() cannot handle microseconds */
377 TicksPerUs
.QuadPart
= 1;
379 UsePerformanceCounter
= FALSE
;
382 Time
->u
.LowPart
= (ULONG
)GetTickCount();
383 Time
->u
.HighPart
= 0;
387 VOID
TimeToMsString(LPSTR String
, LARGE_INTEGER Time
)
390 LARGE_INTEGER LargeTime
;
392 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
394 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
395 strcpy(String
, Convstr
);
396 strcat(String
, "ms");
399 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
401 BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
403 PIPv4_HEADER IpHeader
;
404 PICMP_ECHO_PACKET Icmp
;
407 LARGE_INTEGER RelativeTime
;
408 LARGE_INTEGER LargeTime
;
411 IpHeader
= (PIPv4_HEADER
)buffer
;
413 IphLength
= IpHeader
->IHL
* 4;
415 if (size
< IphLength
+ ICMP_MINSIZE
) {
417 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
422 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
424 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
) {
426 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
431 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId()) {
433 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
438 QueryTime(&LargeTime
);
440 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- Icmp
->Timestamp
.QuadPart
);
442 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1) {
447 TimeToMsString(Time
, RelativeTime
);
451 printf("Reply from %s: bytes=%d time%s%s TTL=%d\n", inet_ntoa(from
->sin_addr
),
452 size
- IphLength
- sizeof(ICMP_ECHO_PACKET
), Sign
, Time
, IpHeader
->TTL
);
453 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
) {
454 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
457 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
458 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
459 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
464 /* Send and receive one ping */
472 PICMP_ECHO_PACKET Packet
;
474 /* Account for extra space for IP header when packet is received */
475 Size
= DataSize
+ 128;
476 Buffer
= GlobalAlloc(0, Size
);
478 printf("Not enough free resources available.\n");
482 ZeroMemory(Buffer
, Size
);
483 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
485 /* Assemble ICMP echo request packet */
486 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
487 Packet
->Icmp
.Code
= 0;
488 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
489 Packet
->Icmp
.SeqNum
= (USHORT
)CurrentSeqNum
;
490 Packet
->Icmp
.Checksum
= 0;
492 /* Timestamp is part of data area */
493 QueryTime(&Packet
->Timestamp
);
495 CopyMemory(Buffer
, &Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
496 /* Calculate checksum for ICMP header and data area */
497 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
501 /* Send ICMP echo request */
504 FD_SET(IcmpSock
, &Fds
);
505 Timeval
.tv_sec
= Timeout
/ 1000;
506 Timeval
.tv_usec
= Timeout
% 1000;
507 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
508 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0)) {
511 printf("Sending packet\n");
512 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
516 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
517 0, (SOCKADDR
*)&Target
, sizeof(Target
));
520 if (Status
== SOCKET_ERROR
) {
521 if (WSAGetLastError() == WSAEHOSTUNREACH
) {
522 printf("Destination host unreachable.\n");
524 printf("Could not transmit data (%d).\n", WSAGetLastError());
530 /* Expect to receive ICMP echo reply */
532 FD_SET(IcmpSock
, &Fds
);
533 Timeval
.tv_sec
= Timeout
/ 1000;
534 Timeval
.tv_usec
= Timeout
% 1000;
536 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
537 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0)) {
538 Length
= sizeof(From
);
539 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
542 printf("Received packet\n");
543 DisplayBuffer(Buffer
, Status
);
547 if (Status
== SOCKET_ERROR
) {
548 if (WSAGetLastError() != WSAETIMEDOUT
) {
549 printf("Could not receive data (%d).\n", WSAGetLastError());
557 printf("Request timed out.\n");
563 if (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
)) {
564 /* FIXME: Wait again as it could be another ICMP message type */
565 printf("Request timed out (incomplete datagram received).\n");
574 /* Program entry point */
575 int main(int argc
, char* argv
[])
584 if ((ParseCmdline(argc
, argv
)) && (Setup())) {
586 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
587 TargetName
, TargetIP
, DataSize
);
590 while ((NeverStop
) || (Count
< PingCount
)) {
598 /* Calculate avarage round trip time */
599 if ((SentCount
- LostCount
) > 0) {
600 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
605 /* Calculate loss percent */
607 Count
= (SentCount
* 100) / LostCount
;
615 TimeToMsString(MinTime
, MinRTT
);
616 TimeToMsString(MaxTime
, MaxRTT
);
617 TimeToMsString(AvgTime
, AvgRTT
);
619 /* Print statistics */
620 printf("\nPing statistics for %s:\n", TargetIP
);
621 printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n",
622 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
623 printf("Approximate round trip times in milli-seconds:\n");
624 printf(" Minimum = %s, Maximum = %s, Average = %s\n",
625 MinTime
, MaxTime
, AvgTime
);