1 /* $Id: ping.c,v 1.4 2001/05/01 23:08:17 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
18 /* FIXME: Where should this be? */
19 #define CopyMemory(Destination, Source, Length) memcpy(Destination, Source, Length);
21 /* Should be in the header files somewhere (exported by ntdll.dll) */
22 long atol(const char *str
);
25 typedef long long __int64
;
28 char * _i64toa(__int64 value
, char *string
, int radix
);
33 /* General ICMP constants */
34 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
35 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
37 /* ICMP message types */
38 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
39 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
43 /* IPv4 header structure */
44 typedef struct _IPv4_HEADER
{
46 unsigned char Version
:4;
48 unsigned short Length
;
50 unsigned short FragFlags
;
52 unsigned char Protocol
;
53 unsigned short Checksum
;
54 unsigned int SrcAddress
;
55 unsigned int DstAddress
;
56 } IPv4_HEADER
, *PIPv4_HEADER
;
58 /* ICMP echo request/reply header structure */
59 typedef struct _ICMP_HEADER
{
62 unsigned short Checksum
;
64 unsigned short SeqNum
;
65 } ICMP_HEADER
, *PICMP_HEADER
;
67 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 usage information on screen */
105 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
106 printf("Options:\n");
107 printf(" -t Ping the specified host until stopped.\n");
108 printf(" To stop - type Control-C.\n");
109 printf(" -n count Number of echo requests to send.\n");
110 printf(" -l size Send buffer size.\n");
111 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
114 /* Reset configuration to default values */
117 LARGE_INTEGER PerformanceCounterFrequency
;
120 ResolveAddresses
= FALSE
;
123 DontFragment
= FALSE
;
127 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
129 if (UsePerformanceCounter
) {
130 /* Performance counters may return incorrect results on some multiprocessor
131 platforms so we restrict execution on the first processor. This may fail
132 on Windows NT so we fall back to GetCurrentTick() for timing */
133 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0) {
134 UsePerformanceCounter
= FALSE
;
137 /* Convert frequency to ticks per millisecond */
138 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
139 /* And to ticks per microsecond */
140 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
142 if (!UsePerformanceCounter
) {
143 /* 1 tick per millisecond for GetCurrentTick() */
144 TicksPerMs
.QuadPart
= 1;
145 /* GetCurrentTick() cannot handle microseconds */
146 TicksPerUs
.QuadPart
= 1;
150 /* Return ULONG in a string */
151 ULONG
GetULONG(LPSTR String
)
157 Length
= strlen(String
);
158 while ((i
< Length
) && ((String
[i
] < '0') || (String
[i
] > '9'))) i
++;
159 if ((i
>= Length
) || ((String
[i
] < '0') || (String
[i
] > '9'))) {
160 InvalidOption
= TRUE
;
163 Value
= (ULONG
)atol(&String
[i
]);
168 /* Return ULONG in a string. Try next paramter if not successful */
169 ULONG
GetULONG2(LPSTR String1
, LPSTR String2
, PINT i
)
173 Value
= GetULONG(String1
);
175 InvalidOption
= FALSE
;
176 if (String2
[0] != '-') {
177 Value
= GetULONG(String2
);
186 /* Parse command line parameters */
187 BOOL
ParseCmdline(int argc
, char* argv
[])
193 lstrcpy(TargetName
, "127.0.0.1");
203 InvalidOption
= FALSE
;
205 for (i
= 1; i
< argc
; i
++) {
206 if (argv
[i
][0] == '-') {
207 switch (argv
[i
][1]) {
208 case 't': NeverStop
= TRUE
; break;
209 case 'a': ResolveAddresses
= TRUE
; break;
210 case 'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
212 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
213 if ((DataSize
< 0) || (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
))) {
214 printf("Bad value for option -l, valid range is from 0 to %d.\n",
215 ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
));
219 case 'f': DontFragment
= TRUE
; break;
220 case 'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
221 case 'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
222 case 'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
224 printf("Bad option %s.\n", argv
[i
]);
229 printf("Bad option format %s.\n", argv
[i
]);
234 printf("Bad parameter %s.\n", argv
[i
]);
237 lstrcpy(TargetName
, argv
[i
]);
243 if ((!ShowUsage
) && (!FoundTarget
)) {
244 printf("Name or IP address of destination host must be specified.\n");
255 /* Calculate checksum of data */
256 WORD
Checksum(PUSHORT data
, UINT size
)
262 size
-= sizeof(USHORT
);
266 sum
+= *(UCHAR
*)data
;
268 sum
= (sum
>> 16) + (sum
& 0xFFFF);
271 return (USHORT
)(~sum
);
274 /* Prepare to ping target */
277 WORD wVersionRequested
;
283 wVersionRequested
= MAKEWORD(2, 2);
285 Status
= WSAStartup(wVersionRequested
, &WsaData
);
287 printf("Could not initialize winsock dll.\n");
291 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
292 if (IcmpSock
== INVALID_SOCKET
) {
293 printf("Could not create socket (#%d).\n", WSAGetLastError());
297 ZeroMemory(&Target
, sizeof(Target
));
299 Addr
= inet_addr(TargetName
);
300 if (Addr
== INADDR_NONE
) {
301 phe
= gethostbyname(TargetName
);
303 printf("Unknown host %s.\n", TargetName
);
309 CopyMemory(&Target
.sin_addr
, phe
->h_addr_list
, phe
->h_length
);
311 Target
.sin_addr
.s_addr
= Addr
;
315 Target
.sin_family
= phe
->h_addrtype
;
317 Target
.sin_family
= AF_INET
;
320 TargetIP
= inet_ntoa(Target
.sin_addr
);
334 if (IcmpSock
!= INVALID_SOCKET
)
335 closesocket(IcmpSock
);
340 VOID
QueryTime(PLARGE_INTEGER Time
)
342 if (UsePerformanceCounter
) {
343 if (QueryPerformanceCounter(Time
) == 0) {
344 /* This should not happen, but we fall
345 back to GetCurrentTick() if it does */
346 Time
->u
.LowPart
= (ULONG
)GetTickCount();
347 Time
->u
.HighPart
= 0;
349 /* 1 tick per millisecond for GetCurrentTick() */
350 TicksPerMs
.QuadPart
= 1;
351 /* GetCurrentTick() cannot handle microseconds */
352 TicksPerUs
.QuadPart
= 1;
354 UsePerformanceCounter
= FALSE
;
357 Time
->u
.LowPart
= (ULONG
)GetTickCount();
358 Time
->u
.HighPart
= 0;
362 VOID
TimeToMsString(LPSTR String
, LARGE_INTEGER Time
)
366 LARGE_INTEGER LargeTime
;
368 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
369 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
370 strcpy(String
, Convstr
);
373 LargeTime
.QuadPart
= (Time
.QuadPart
% TicksPerMs
.QuadPart
) / TicksPerUs
.QuadPart
;
374 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
375 Length
= strlen(Convstr
);
377 for (i
= 0; i
< 4 - Length
; i
++)
381 strcat(String
, Convstr
);
382 strcat(String
, "ms");
385 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
387 BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
389 PIPv4_HEADER IpHeader
;
390 PICMP_ECHO_PACKET Icmp
;
393 LARGE_INTEGER RelativeTime
;
394 LARGE_INTEGER LargeTime
;
396 IpHeader
= (PIPv4_HEADER
)buffer
;
398 IphLength
= IpHeader
->IHL
* 4;
400 if (size
< IphLength
+ ICMP_MINSIZE
)
403 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
405 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
408 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
411 QueryTime(&LargeTime
);
413 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- Icmp
->Timestamp
.QuadPart
);
415 TimeToMsString(Time
, RelativeTime
);
417 printf("Reply from %s: bytes=%d time=%s TTL=%d\n", inet_ntoa(from
->sin_addr
),
418 size
- IphLength
- sizeof(ICMP_ECHO_PACKET
) , Time
, IpHeader
->TTL
);
419 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
) {
420 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
423 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
424 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
425 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
430 /* Send and receive one ping */
438 PICMP_ECHO_PACKET Packet
;
440 /* Account for extra space for IP header when packet is received */
441 Size
= DataSize
+ 128;
442 Buffer
= GlobalAlloc(0, Size
);
444 printf("Not enough free resources available.\n");
448 ZeroMemory(Buffer
, Size
);
449 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
451 /* Assemble ICMP echo request packet */
452 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
453 Packet
->Icmp
.Code
= 0;
454 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
455 Packet
->Icmp
.SeqNum
= (USHORT
)CurrentSeqNum
;
456 Packet
->Icmp
.Checksum
= 0;
458 /* Timestamp is part of data area */
459 QueryTime(&Packet
->Timestamp
);
461 CopyMemory(Buffer
, &Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
462 /* Calculate checksum for ICMP header and data area */
463 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
467 /* Send ICMP echo request */
470 FD_SET(IcmpSock
, &Fds
);
471 Timeval
.tv_sec
= Timeout
/ 1000;
472 Timeval
.tv_usec
= Timeout
% 1000;
473 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
474 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0)) {
475 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
476 0, (SOCKADDR
*)&Target
, sizeof(Target
));
479 if (Status
== SOCKET_ERROR
) {
480 if (WSAGetLastError() == WSAEHOSTUNREACH
) {
481 printf("Destination host unreachable.\n");
483 printf("Could not transmit data (%d).\n", WSAGetLastError());
489 /* Expect to receive ICMP echo reply */
491 FD_SET(IcmpSock
, &Fds
);
492 Timeval
.tv_sec
= Timeout
/ 1000;
493 Timeval
.tv_usec
= Timeout
% 1000;
495 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
496 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0)) {
497 Length
= sizeof(From
);
498 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
500 if (Status
== SOCKET_ERROR
) {
501 if (WSAGetLastError() != WSAETIMEDOUT
) {
502 printf("Could not receive data (%d).\n", WSAGetLastError());
510 printf("Request timed out.\n");
516 if (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
)) {
517 /* FIXME: Wait again as it could be another ICMP message type */
518 printf("Request timed out.\n");
527 /* Program entry point */
528 int main(int argc
, char* argv
[])
537 if ((ParseCmdline(argc
, argv
)) && (Setup())) {
539 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
540 TargetName
, TargetIP
, DataSize
);
543 while ((NeverStop
) || (Count
< PingCount
)) {
551 /* Calculate avarage round trip time */
552 if ((SentCount
- LostCount
) > 0) {
553 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
558 /* Calculate loss percent */
560 Count
= (SentCount
* 100) / LostCount
;
568 TimeToMsString(MinTime
, MinRTT
);
569 TimeToMsString(MaxTime
, MaxRTT
);
570 TimeToMsString(AvgTime
, AvgRTT
);
572 /* Print statistics */
573 printf("\nPing statistics for %s:\n", TargetIP
);
574 printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n",
575 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
576 printf("Approximate round trip times in milli-seconds:\n");
577 printf(" Minimum = %s, Maximum = %s, Average = %s\n",
578 MinTime
, MaxTime
, AvgTime
);