1 /* $Id: ping.c,v 1.5 2001/06/15 17:48:43 chorns Exp $
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
20 /* FIXME: Where should this be? */
21 #define CopyMemory(Destination, Source, Length) memcpy(Destination, Source, Length);
23 /* Should be in the header files somewhere (exported by ntdll.dll) */
24 long atol(const char *str
);
27 typedef long long __int64
;
30 char * _i64toa(__int64 value
, char *string
, int radix
);
35 /* General ICMP constants */
36 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
37 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
39 /* ICMP message types */
40 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
41 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
45 /* IPv4 header structure */
46 typedef struct _IPv4_HEADER
{
48 unsigned char Version
:4;
50 unsigned short Length
;
52 unsigned short FragFlags
;
54 unsigned char Protocol
;
55 unsigned short Checksum
;
56 unsigned int SrcAddress
;
57 unsigned int DstAddress
;
58 } IPv4_HEADER
, *PIPv4_HEADER
;
60 /* ICMP echo request/reply header structure */
61 typedef struct _ICMP_HEADER
{
64 unsigned short Checksum
;
66 unsigned short SeqNum
;
67 } ICMP_HEADER
, *PICMP_HEADER
;
69 typedef struct _ICMP_ECHO_PACKET
{
71 LARGE_INTEGER Timestamp
;
72 } ICMP_ECHO_PACKET
, *PICMP_ECHO_PACKET
;
78 BOOL ResolveAddresses
;
80 UINT DataSize
; /* ICMP echo request data size */
95 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
99 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
100 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
101 BOOL UsePerformanceCounter
;
104 /* Display the contents of a buffer */
112 printf("Buffer (0x%X) Size (0x%X).\n", Buffer
, Size
);
115 for (i
= 0; i
< Size
; i
++) {
119 printf("%02X ", (p
[i
]) & 0xFF);
123 /* Display usage information on screen */
126 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
127 printf("Options:\n");
128 printf(" -t Ping the specified host until stopped.\n");
129 printf(" To stop - type Control-C.\n");
130 printf(" -n count Number of echo requests to send.\n");
131 printf(" -l size Send buffer size.\n");
132 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
135 /* Reset configuration to default values */
138 LARGE_INTEGER PerformanceCounterFrequency
;
141 ResolveAddresses
= FALSE
;
144 DontFragment
= FALSE
;
148 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
150 if (UsePerformanceCounter
) {
151 /* Performance counters may return incorrect results on some multiprocessor
152 platforms so we restrict execution on the first processor. This may fail
153 on Windows NT so we fall back to GetCurrentTick() for timing */
154 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0) {
155 UsePerformanceCounter
= FALSE
;
158 /* Convert frequency to ticks per millisecond */
159 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
160 /* And to ticks per microsecond */
161 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
163 if (!UsePerformanceCounter
) {
164 /* 1 tick per millisecond for GetCurrentTick() */
165 TicksPerMs
.QuadPart
= 1;
166 /* GetCurrentTick() cannot handle microseconds */
167 TicksPerUs
.QuadPart
= 1;
171 /* Return ULONG in a string */
172 ULONG
GetULONG(LPSTR String
)
178 Length
= strlen(String
);
179 while ((i
< Length
) && ((String
[i
] < '0') || (String
[i
] > '9'))) i
++;
180 if ((i
>= Length
) || ((String
[i
] < '0') || (String
[i
] > '9'))) {
181 InvalidOption
= TRUE
;
184 Value
= (ULONG
)atol(&String
[i
]);
189 /* Return ULONG in a string. Try next paramter if not successful */
190 ULONG
GetULONG2(LPSTR String1
, LPSTR String2
, PINT i
)
194 Value
= GetULONG(String1
);
196 InvalidOption
= FALSE
;
197 if (String2
[0] != '-') {
198 Value
= GetULONG(String2
);
207 /* Parse command line parameters */
208 BOOL
ParseCmdline(int argc
, char* argv
[])
214 lstrcpy(TargetName
, "127.0.0.1");
224 InvalidOption
= FALSE
;
226 for (i
= 1; i
< argc
; i
++) {
227 if (argv
[i
][0] == '-') {
228 switch (argv
[i
][1]) {
229 case 't': NeverStop
= TRUE
; break;
230 case 'a': ResolveAddresses
= TRUE
; break;
231 case 'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
233 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
234 if ((DataSize
< 0) || (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
))) {
235 printf("Bad value for option -l, valid range is from 0 to %d.\n",
236 ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
));
240 case 'f': DontFragment
= TRUE
; break;
241 case 'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
242 case 'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
243 case 'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
245 printf("Bad option %s.\n", argv
[i
]);
250 printf("Bad option format %s.\n", argv
[i
]);
255 printf("Bad parameter %s.\n", argv
[i
]);
258 lstrcpy(TargetName
, argv
[i
]);
264 if ((!ShowUsage
) && (!FoundTarget
)) {
265 printf("Name or IP address of destination host must be specified.\n");
276 /* Calculate checksum of data */
277 WORD
Checksum(PUSHORT data
, UINT size
)
283 size
-= sizeof(USHORT
);
287 sum
+= *(UCHAR
*)data
;
289 sum
= (sum
>> 16) + (sum
& 0xFFFF);
292 return (USHORT
)(~sum
);
295 /* Prepare to ping target */
298 WORD wVersionRequested
;
304 wVersionRequested
= MAKEWORD(2, 2);
306 Status
= WSAStartup(wVersionRequested
, &WsaData
);
308 printf("Could not initialize winsock dll.\n");
312 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
313 if (IcmpSock
== INVALID_SOCKET
) {
314 printf("Could not create socket (#%d).\n", WSAGetLastError());
318 ZeroMemory(&Target
, sizeof(Target
));
320 Addr
= inet_addr(TargetName
);
321 if (Addr
== INADDR_NONE
) {
322 phe
= gethostbyname(TargetName
);
324 printf("Unknown host %s.\n", TargetName
);
330 CopyMemory(&Target
.sin_addr
, phe
->h_addr_list
, phe
->h_length
);
332 Target
.sin_addr
.s_addr
= Addr
;
336 Target
.sin_family
= phe
->h_addrtype
;
338 Target
.sin_family
= AF_INET
;
341 TargetIP
= inet_ntoa(Target
.sin_addr
);
355 if (IcmpSock
!= INVALID_SOCKET
)
356 closesocket(IcmpSock
);
361 VOID
QueryTime(PLARGE_INTEGER Time
)
363 if (UsePerformanceCounter
) {
364 if (QueryPerformanceCounter(Time
) == 0) {
365 /* This should not happen, but we fall
366 back to GetCurrentTick() if it does */
367 Time
->u
.LowPart
= (ULONG
)GetTickCount();
368 Time
->u
.HighPart
= 0;
370 /* 1 tick per millisecond for GetCurrentTick() */
371 TicksPerMs
.QuadPart
= 1;
372 /* GetCurrentTick() cannot handle microseconds */
373 TicksPerUs
.QuadPart
= 1;
375 UsePerformanceCounter
= FALSE
;
378 Time
->u
.LowPart
= (ULONG
)GetTickCount();
379 Time
->u
.HighPart
= 0;
383 VOID
TimeToMsString(LPSTR String
, LARGE_INTEGER Time
)
387 LARGE_INTEGER LargeTime
;
389 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
390 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
391 strcpy(String
, Convstr
);
394 LargeTime
.QuadPart
= (Time
.QuadPart
% TicksPerMs
.QuadPart
) / TicksPerUs
.QuadPart
;
395 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
396 Length
= strlen(Convstr
);
398 for (i
= 0; i
< 4 - Length
; i
++)
402 strcat(String
, Convstr
);
403 strcat(String
, "ms");
406 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
408 BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
410 PIPv4_HEADER IpHeader
;
411 PICMP_ECHO_PACKET Icmp
;
414 LARGE_INTEGER RelativeTime
;
415 LARGE_INTEGER LargeTime
;
417 IpHeader
= (PIPv4_HEADER
)buffer
;
419 IphLength
= IpHeader
->IHL
* 4;
421 if (size
< IphLength
+ ICMP_MINSIZE
) {
423 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
428 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
430 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
) {
432 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
437 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId()) {
439 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
444 QueryTime(&LargeTime
);
446 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- Icmp
->Timestamp
.QuadPart
);
448 TimeToMsString(Time
, RelativeTime
);
450 printf("Reply from %s: bytes=%d time=%s TTL=%d\n", inet_ntoa(from
->sin_addr
),
451 size
- IphLength
- sizeof(ICMP_ECHO_PACKET
), Time
, IpHeader
->TTL
);
452 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
) {
453 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
456 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
457 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
458 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
463 /* Send and receive one ping */
471 PICMP_ECHO_PACKET Packet
;
473 /* Account for extra space for IP header when packet is received */
474 Size
= DataSize
+ 128;
475 Buffer
= GlobalAlloc(0, Size
);
477 printf("Not enough free resources available.\n");
481 ZeroMemory(Buffer
, Size
);
482 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
484 /* Assemble ICMP echo request packet */
485 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
486 Packet
->Icmp
.Code
= 0;
487 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
488 Packet
->Icmp
.SeqNum
= (USHORT
)CurrentSeqNum
;
489 Packet
->Icmp
.Checksum
= 0;
491 /* Timestamp is part of data area */
492 QueryTime(&Packet
->Timestamp
);
494 CopyMemory(Buffer
, &Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
495 /* Calculate checksum for ICMP header and data area */
496 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
500 /* Send ICMP echo request */
503 FD_SET(IcmpSock
, &Fds
);
504 Timeval
.tv_sec
= Timeout
/ 1000;
505 Timeval
.tv_usec
= Timeout
% 1000;
506 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
507 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0)) {
510 printf("Sending packet\n");
511 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
515 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
516 0, (SOCKADDR
*)&Target
, sizeof(Target
));
519 if (Status
== SOCKET_ERROR
) {
520 if (WSAGetLastError() == WSAEHOSTUNREACH
) {
521 printf("Destination host unreachable.\n");
523 printf("Could not transmit data (%d).\n", WSAGetLastError());
529 /* Expect to receive ICMP echo reply */
531 FD_SET(IcmpSock
, &Fds
);
532 Timeval
.tv_sec
= Timeout
/ 1000;
533 Timeval
.tv_usec
= Timeout
% 1000;
535 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
536 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0)) {
537 Length
= sizeof(From
);
538 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
541 printf("Received packet\n");
542 DisplayBuffer(Buffer
, Status
);
546 if (Status
== SOCKET_ERROR
) {
547 if (WSAGetLastError() != WSAETIMEDOUT
) {
548 printf("Could not receive data (%d).\n", WSAGetLastError());
556 printf("Request timed out.\n");
562 if (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
)) {
563 /* FIXME: Wait again as it could be another ICMP message type */
564 printf("Request timed out (incomplete datagram received).\n");
573 /* Program entry point */
574 int main(int argc
, char* argv
[])
583 if ((ParseCmdline(argc
, argv
)) && (Setup())) {
585 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
586 TargetName
, TargetIP
, DataSize
);
589 while ((NeverStop
) || (Count
< PingCount
)) {
597 /* Calculate avarage round trip time */
598 if ((SentCount
- LostCount
) > 0) {
599 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
604 /* Calculate loss percent */
606 Count
= (SentCount
* 100) / LostCount
;
614 TimeToMsString(MinTime
, MinRTT
);
615 TimeToMsString(MaxTime
, MaxRTT
);
616 TimeToMsString(AvgTime
, AvgRTT
);
618 /* Print statistics */
619 printf("\nPing statistics for %s:\n", TargetIP
);
620 printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n",
621 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
622 printf("Approximate round trip times in milli-seconds:\n");
623 printf(" Minimum = %s, Maximum = %s, Average = %s\n",
624 MinTime
, MaxTime
, AvgTime
);