2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS ping utility
4 * FILE: base/applications/network/ping/ping.c
5 * PURPOSE: Network test utility
19 /* Should be in the header files somewhere (exported by ntdll.dll) */
20 long atol(const char *str
);
23 typedef long long __int64
;
26 char * _i64toa(__int64 value
, char *string
, int radix
);
30 /* General ICMP constants */
31 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
32 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
34 /* ICMP message types */
35 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
36 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
40 /* IPv4 header structure */
41 typedef struct _IPv4_HEADER
44 unsigned char Version
:4;
46 unsigned short Length
;
48 unsigned short FragFlags
;
50 unsigned char Protocol
;
51 unsigned short Checksum
;
52 unsigned int SrcAddress
;
53 unsigned int DstAddress
;
54 } IPv4_HEADER
, *PIPv4_HEADER
;
56 /* ICMP echo request/reply header structure */
57 typedef struct _ICMP_HEADER
61 unsigned short Checksum
;
63 unsigned short SeqNum
;
64 } ICMP_HEADER
, *PICMP_HEADER
;
66 typedef struct _ICMP_ECHO_PACKET
69 LARGE_INTEGER Timestamp
;
70 } ICMP_ECHO_PACKET
, *PICMP_ECHO_PACKET
;
76 BOOL ResolveAddresses
;
78 UINT DataSize
; /* ICMP echo request data size */
93 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
97 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
98 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
99 BOOL UsePerformanceCounter
;
102 /* Display the contents of a buffer */
103 static VOID
DisplayBuffer(
110 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
113 for (i
= 0; i
< Size
; i
++)
117 printf("%02X ", (p
[i
]) & 0xFF);
122 /* Display usage information on screen */
123 static VOID
Usage(VOID
)
125 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
126 printf("Options:\n");
127 printf(" -t Ping the specified host until stopped.\n");
128 printf(" To stop - type Control-C.\n");
129 printf(" -n count Number of echo requests to send.\n");
130 printf(" -l size Send buffer size.\n");
131 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
134 /* Reset configuration to default values */
135 static VOID
Reset(VOID
)
137 LARGE_INTEGER PerformanceCounterFrequency
;
140 ResolveAddresses
= FALSE
;
143 DontFragment
= FALSE
;
147 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
149 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
;
157 /* Convert frequency to ticks per millisecond */
158 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
159 /* And to ticks per microsecond */
160 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
162 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 static ULONG
GetULONG(LPSTR String
)
178 Length
= (UINT
)_tcslen(String
);
179 while ((i
< Length
) && ((String
[i
] < '0') || (String
[i
] > '9'))) i
++;
180 if ((i
>= Length
) || ((String
[i
] < '0') || (String
[i
] > '9')))
182 InvalidOption
= TRUE
;
185 Value
= strtoul(&String
[i
], &StopString
, 10);
190 /* Return ULONG in a string. Try next paramter if not successful */
191 static ULONG
GetULONG2(LPSTR String1
, LPSTR String2
, PINT i
)
195 Value
= GetULONG(String1
);
198 InvalidOption
= FALSE
;
199 if (String2
[0] != '-')
201 Value
= GetULONG(String2
);
210 /* Parse command line parameters */
211 static BOOL
ParseCmdline(int argc
, char* argv
[])
221 InvalidOption
= FALSE
;
223 for (i
= 1; i
< argc
; i
++)
225 if (argv
[i
][0] == '-')
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
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
))
236 printf("Bad value for option -l, valid range is from 0 to %I64d.\n",
237 ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
));
241 case 'f': DontFragment
= TRUE
; break;
242 case 'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
243 case 'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
244 case 'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
246 printf("Bad option %s.\n", argv
[i
]);
252 printf("Bad option format %s.\n", argv
[i
]);
260 printf("Bad parameter %s.\n", argv
[i
]);
265 lstrcpy(TargetName
, argv
[i
]);
271 if ((!ShowUsage
) && (!FoundTarget
))
273 printf("Name or IP address of destination host must be specified.\n");
285 /* Calculate checksum of data */
286 static WORD
Checksum(PUSHORT data
, UINT size
)
293 size
-= sizeof(USHORT
);
297 sum
+= *(UCHAR
*)data
;
299 sum
= (sum
>> 16) + (sum
& 0xFFFF);
302 return (USHORT
)(~sum
);
305 /* Prepare to ping target */
306 static BOOL
Setup(VOID
)
308 WORD wVersionRequested
;
314 wVersionRequested
= MAKEWORD(2, 2);
316 Status
= WSAStartup(wVersionRequested
, &WsaData
);
319 printf("Could not initialize winsock dll.\n");
323 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
324 if (IcmpSock
== INVALID_SOCKET
)
326 printf("Could not create socket (#%d).\n", WSAGetLastError());
330 ZeroMemory(&Target
, sizeof(Target
));
332 Addr
= inet_addr(TargetName
);
333 if (Addr
== INADDR_NONE
)
335 phe
= gethostbyname(TargetName
);
338 printf("Unknown host %s.\n", TargetName
);
344 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
346 Target
.sin_addr
.s_addr
= Addr
;
349 Target
.sin_family
= phe
->h_addrtype
;
351 Target
.sin_family
= AF_INET
;
353 TargetIP
= inet_ntoa(Target
.sin_addr
);
365 static VOID
Cleanup(VOID
)
367 if (IcmpSock
!= INVALID_SOCKET
)
368 closesocket(IcmpSock
);
373 static VOID
QueryTime(PLARGE_INTEGER Time
)
375 if (UsePerformanceCounter
)
377 if (QueryPerformanceCounter(Time
) == 0)
379 /* This should not happen, but we fall
380 back to GetCurrentTick() if it does */
381 Time
->u
.LowPart
= (ULONG
)GetTickCount();
382 Time
->u
.HighPart
= 0;
384 /* 1 tick per millisecond for GetCurrentTick() */
385 TicksPerMs
.QuadPart
= 1;
386 /* GetCurrentTick() cannot handle microseconds */
387 TicksPerUs
.QuadPart
= 1;
389 UsePerformanceCounter
= FALSE
;
394 Time
->u
.LowPart
= (ULONG
)GetTickCount();
395 Time
->u
.HighPart
= 0;
399 static VOID
TimeToMsString(LPSTR String
, LARGE_INTEGER Time
)
402 LARGE_INTEGER LargeTime
;
404 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
406 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
407 strcpy(String
, Convstr
);
408 strcat(String
, "ms");
411 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
413 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
415 PIPv4_HEADER IpHeader
;
416 PICMP_ECHO_PACKET Icmp
;
419 LARGE_INTEGER RelativeTime
;
420 LARGE_INTEGER LargeTime
;
423 IpHeader
= (PIPv4_HEADER
)buffer
;
425 IphLength
= IpHeader
->IHL
* 4;
427 if (size
< IphLength
+ ICMP_MINSIZE
)
430 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
435 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
437 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
440 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
445 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
448 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
453 QueryTime(&LargeTime
);
455 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- Icmp
->Timestamp
.QuadPart
);
457 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
465 TimeToMsString(Time
, RelativeTime
);
469 printf("Reply from %s: bytes=%I64d time%s%s TTL=%d\n", inet_ntoa(from
->sin_addr
),
470 size
- IphLength
- sizeof(ICMP_ECHO_PACKET
), Sign
, Time
, IpHeader
->TTL
);
471 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
)
473 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
476 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
477 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
479 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
484 /* Send and receive one ping */
485 static BOOL
Ping(VOID
)
492 PICMP_ECHO_PACKET Packet
;
494 /* Account for extra space for IP header when packet is received */
495 Size
= DataSize
+ 128;
496 Buffer
= GlobalAlloc(0, Size
);
499 printf("Not enough free resources available.\n");
503 ZeroMemory(Buffer
, Size
);
504 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
506 /* Assemble ICMP echo request packet */
507 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
508 Packet
->Icmp
.Code
= 0;
509 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
510 Packet
->Icmp
.SeqNum
= htons((USHORT
)CurrentSeqNum
);
511 Packet
->Icmp
.Checksum
= 0;
513 /* Timestamp is part of data area */
514 QueryTime(&Packet
->Timestamp
);
516 CopyMemory(Buffer
, &Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
517 /* Calculate checksum for ICMP header and data area */
518 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
522 /* Send ICMP echo request */
525 FD_SET(IcmpSock
, &Fds
);
526 Timeval
.tv_sec
= Timeout
/ 1000;
527 Timeval
.tv_usec
= Timeout
% 1000;
528 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
529 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
533 printf("Sending packet\n");
534 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
538 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
539 0, (SOCKADDR
*)&Target
, sizeof(Target
));
542 if (Status
== SOCKET_ERROR
)
545 if (WSAGetLastError() == WSAEHOSTUNREACH
)
546 printf("Destination host unreachable.\n");
548 printf("Could not transmit data (%d).\n", WSAGetLastError());
553 /* Expect to receive ICMP echo reply */
555 FD_SET(IcmpSock
, &Fds
);
556 Timeval
.tv_sec
= Timeout
/ 1000;
557 Timeval
.tv_usec
= Timeout
% 1000;
559 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
560 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
562 Length
= sizeof(From
);
563 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
566 printf("Received packet\n");
567 DisplayBuffer(Buffer
, Status
);
573 if (Status
== SOCKET_ERROR
)
575 if (WSAGetLastError() != WSAETIMEDOUT
)
577 printf("Could not receive data (%d).\n", WSAGetLastError());
586 printf("Request timed out.\n");
591 if (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
))
593 /* FIXME: Wait again as it could be another ICMP message type */
594 printf("Request timed out (incomplete datagram received).\n");
603 /* Program entry point */
604 int main(int argc
, char* argv
[])
613 if ((ParseCmdline(argc
, argv
)) && (Setup()))
616 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
617 TargetName
, TargetIP
, DataSize
);
620 while ((NeverStop
) || (Count
< PingCount
))
629 /* Calculate avarage round trip time */
630 if ((SentCount
- LostCount
) > 0)
631 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
635 /* Calculate loss percent */
636 Count
= SentCount
? (LostCount
* 100) / SentCount
: 0;
641 TimeToMsString(MinTime
, MinRTT
);
642 TimeToMsString(MaxTime
, MaxRTT
);
643 TimeToMsString(AvgTime
, AvgRTT
);
645 /* Print statistics */
646 printf("\nPing statistics for %s:\n", TargetIP
);
647 printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n",
648 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
649 /* Print approximate times or NO approximate times if 100% loss */
650 if ((SentCount
- LostCount
) > 0)
652 printf("Approximate round trip times in milli-seconds:\n");
653 printf(" Minimum = %s, Maximum = %s, Average = %s\n",
654 MinTime
, MaxTime
, AvgTime
);