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
9 #define WIN32_NO_STATUS
25 /* General ICMP constants */
26 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
27 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
29 /* ICMP message types */
30 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
31 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
35 /* IPv4 header structure */
36 typedef struct _IPv4_HEADER
39 unsigned char Version
:4;
41 unsigned short Length
;
43 unsigned short FragFlags
;
45 unsigned char Protocol
;
46 unsigned short Checksum
;
47 unsigned int SrcAddress
;
48 unsigned int DstAddress
;
49 } IPv4_HEADER
, *PIPv4_HEADER
;
51 /* ICMP echo request/reply header structure */
52 typedef struct _ICMP_HEADER
56 unsigned short Checksum
;
58 unsigned short SeqNum
;
59 } ICMP_HEADER
, *PICMP_HEADER
;
61 typedef struct _ICMP_ECHO_PACKET
64 } ICMP_ECHO_PACKET
, *PICMP_ECHO_PACKET
;
69 BOOL ResolveAddresses
;
71 UINT DataSize
; /* ICMP echo request data size */
76 WCHAR TargetName
[256];
86 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
90 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
91 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
92 LARGE_INTEGER SentTime
;
93 BOOL UsePerformanceCounter
;
97 /* Display the contents of a buffer */
98 static VOID
DisplayBuffer(
105 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
108 for (i
= 0; i
< Size
; i
++)
112 printf("%02X ", (p
[i
]) & 0xFF);
118 MyLoadString(UINT uID
)
125 hres
= FindResourceW(NULL
, MAKEINTRESOURCEW((LOWORD(uID
) >> 4) + 1), RT_STRING
);
126 if (!hres
) return NULL
;
128 hResData
= LoadResource(NULL
, hres
);
129 if (!hResData
) return NULL
;
131 pwsz
= LockResource(hResData
);
132 if (!pwsz
) return NULL
;
134 string_num
= uID
& 15;
135 for (i
= 0; i
< string_num
; i
++)
141 void FormatOutput(UINT uID
, ...)
151 va_start(valist
, uID
);
153 Format
= MyLoadString(uID
);
156 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
, Format
, 0, 0, Buf
,\
157 sizeof(Buf
) / sizeof(WCHAR
), &valist
);
161 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
164 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
|\
165 FORMAT_MESSAGE_ALLOCATE_BUFFER
,\
166 Format
, 0, 0, (LPWSTR
)&pBuf
, 0, &valist
);
172 WriteConsole(hStdOutput
, pBuf
, DataLength
, &written
, NULL
);
178 /* Display usage information on screen */
179 static VOID
Usage(VOID
)
181 FormatOutput(IDS_USAGE
);
184 /* Reset configuration to default values */
185 static VOID
Reset(VOID
)
187 LARGE_INTEGER PerformanceCounterFrequency
;
190 ResolveAddresses
= FALSE
;
193 DontFragment
= FALSE
;
197 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
199 if (UsePerformanceCounter
)
201 /* Performance counters may return incorrect results on some multiprocessor
202 platforms so we restrict execution on the first processor. This may fail
203 on Windows NT so we fall back to GetCurrentTick() for timing */
204 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
205 UsePerformanceCounter
= FALSE
;
207 /* Convert frequency to ticks per millisecond */
208 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
209 /* And to ticks per microsecond */
210 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
212 if (!UsePerformanceCounter
)
214 /* 1 tick per millisecond for GetCurrentTick() */
215 TicksPerMs
.QuadPart
= 1;
216 /* GetCurrentTick() cannot handle microseconds */
217 TicksPerUs
.QuadPart
= 1;
221 /* Parse command line parameters */
222 static BOOL
ParseCmdline(int argc
, LPWSTR argv
[])
225 BOOL FoundTarget
= FALSE
, InvalidOption
= FALSE
;
233 for (i
= 1; i
< argc
; i
++)
235 if (argv
[i
][0] == L
'-' || argv
[i
][0] == L
'/')
239 case L
't': NeverStop
= TRUE
; break;
240 case L
'a': ResolveAddresses
= TRUE
; break;
243 PingCount
= wcstoul(argv
[++i
], NULL
, 0);
245 InvalidOption
= TRUE
;
250 DataSize
= wcstoul(argv
[++i
], NULL
, 0);
252 if (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
) - sizeof(IPv4_HEADER
))
254 FormatOutput(IDS_BAD_VALUE_OPTION_L
, ICMP_MAXSIZE
- \
255 (int)sizeof(ICMP_ECHO_PACKET
) - \
256 (int)sizeof(IPv4_HEADER
));
260 InvalidOption
= TRUE
;
262 case L
'f': DontFragment
= TRUE
; break;
265 TTLValue
= wcstoul(argv
[++i
], NULL
, 0);
267 InvalidOption
= TRUE
;
271 TOSValue
= wcstoul(argv
[++i
], NULL
, 0);
273 InvalidOption
= TRUE
;
277 Timeout
= wcstoul(argv
[++i
], NULL
, 0);
279 InvalidOption
= TRUE
;
285 FormatOutput(IDS_BAD_OPTION
, argv
[i
]);
290 FormatOutput(IDS_BAD_OPTION_FORMAT
, argv
[i
]);
298 FormatOutput(IDS_BAD_PARAMETER
, argv
[i
]);
303 wcscpy(TargetName
, argv
[i
]);
311 FormatOutput(IDS_DEST_MUST_BE_SPECIFIED
);
318 /* Calculate checksum of data */
319 static WORD
Checksum(PUSHORT data
, UINT size
)
326 size
-= sizeof(USHORT
);
330 sum
+= *(UCHAR
*)data
;
332 sum
= (sum
>> 16) + (sum
& 0xFFFF);
335 return (USHORT
)(~sum
);
338 /* Prepare to ping target */
339 static BOOL
Setup(VOID
)
341 WORD wVersionRequested
;
346 CHAR aTargetName
[256];
348 wVersionRequested
= MAKEWORD(2, 2);
350 Status
= WSAStartup(wVersionRequested
, &WsaData
);
353 FormatOutput(IDS_COULD_NOT_INIT_WINSOCK
);
357 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
358 if (IcmpSock
== INVALID_SOCKET
)
360 FormatOutput(IDS_COULD_NOT_CREATE_SOCKET
, WSAGetLastError());
364 if (setsockopt(IcmpSock
,
367 (const char *)&DontFragment
,
368 sizeof(DontFragment
)) == SOCKET_ERROR
)
370 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
374 if (setsockopt(IcmpSock
,
377 (const char *)&TTLValue
,
378 sizeof(TTLValue
)) == SOCKET_ERROR
)
380 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
385 if(!WideCharToMultiByte(CP_ACP
, 0, TargetName
, -1, aTargetName
,\
386 sizeof(aTargetName
), NULL
, NULL
))
388 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
392 ZeroMemory(&Target
, sizeof(Target
));
394 Addr
= inet_addr(aTargetName
);
395 if (Addr
== INADDR_NONE
)
397 phe
= gethostbyname(aTargetName
);
400 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
404 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
405 Target
.sin_family
= phe
->h_addrtype
;
409 Target
.sin_addr
.s_addr
= Addr
;
410 Target
.sin_family
= AF_INET
;
414 swprintf(TargetIP
, L
"%d.%d.%d.%d", Target
.sin_addr
.S_un
.S_un_b
.s_b1
,\
415 Target
.sin_addr
.S_un
.S_un_b
.s_b2
,\
416 Target
.sin_addr
.S_un
.S_un_b
.s_b3
,\
417 Target
.sin_addr
.S_un
.S_un_b
.s_b4
);
429 static VOID
Cleanup(VOID
)
431 if (IcmpSock
!= INVALID_SOCKET
)
432 closesocket(IcmpSock
);
437 static VOID
QueryTime(PLARGE_INTEGER Time
)
439 if (UsePerformanceCounter
)
441 if (QueryPerformanceCounter(Time
) == 0)
443 /* This should not happen, but we fall
444 back to GetCurrentTick() if it does */
445 Time
->u
.LowPart
= (ULONG
)GetTickCount();
446 Time
->u
.HighPart
= 0;
448 /* 1 tick per millisecond for GetCurrentTick() */
449 TicksPerMs
.QuadPart
= 1;
450 /* GetCurrentTick() cannot handle microseconds */
451 TicksPerUs
.QuadPart
= 1;
453 UsePerformanceCounter
= FALSE
;
458 Time
->u
.LowPart
= (ULONG
)GetTickCount();
459 Time
->u
.HighPart
= 0;
463 static VOID
TimeToMsString(LPWSTR String
, LARGE_INTEGER Time
)
466 LARGE_INTEGER LargeTime
;
469 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
471 _i64tow(LargeTime
.QuadPart
, Convstr
, 10);
472 wcscpy(String
, Convstr
);
473 ms
= MyLoadString(IDS_MS
);
477 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
479 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
481 PIPv4_HEADER IpHeader
;
482 PICMP_ECHO_PACKET Icmp
;
485 LARGE_INTEGER RelativeTime
;
486 LARGE_INTEGER LargeTime
;
490 IpHeader
= (PIPv4_HEADER
)buffer
;
492 IphLength
= IpHeader
->IHL
* 4;
494 if (size
< IphLength
+ ICMP_MINSIZE
)
497 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
502 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
504 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
507 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
512 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
515 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
520 if (from
->sin_addr
.s_addr
!= Target
.sin_addr
.s_addr
)
523 printf("Bad source address (%s should be %s)\n", inet_ntoa(from
->sin_addr
), inet_ntoa(Target
.sin_addr
));
528 QueryTime(&LargeTime
);
530 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- SentTime
.QuadPart
);
532 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
537 ms1
= MyLoadString(IDS_1MS
);
543 TimeToMsString(Time
, RelativeTime
);
547 swprintf(wfromIP
, L
"%d.%d.%d.%d", from
->sin_addr
.S_un
.S_un_b
.s_b1
,\
548 from
->sin_addr
.S_un
.S_un_b
.s_b2
,\
549 from
->sin_addr
.S_un
.S_un_b
.s_b3
,\
550 from
->sin_addr
.S_un
.S_un_b
.s_b4
);
551 FormatOutput(IDS_REPLY_FROM
, wfromIP
,\
552 size
- IphLength
- (int)sizeof(ICMP_ECHO_PACKET
),\
553 Sign
, Time
, IpHeader
->TTL
);
555 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
)
557 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
560 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
561 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
563 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
568 /* Send and receive one ping */
569 static BOOL
Ping(VOID
)
576 PICMP_ECHO_PACKET Packet
;
578 /* Account for extra space for IP header when packet is received */
579 Size
= DataSize
+ 128;
580 Buffer
= GlobalAlloc(0, Size
);
583 FormatOutput(IDS_NOT_ENOUGH_RESOURCES
);
587 ZeroMemory(Buffer
, Size
);
588 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
590 /* Assemble ICMP echo request packet */
591 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
592 Packet
->Icmp
.Code
= 0;
593 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
594 Packet
->Icmp
.SeqNum
= htons((USHORT
)CurrentSeqNum
);
595 Packet
->Icmp
.Checksum
= 0;
597 /* Calculate checksum for ICMP header and data area */
598 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
602 /* Send ICMP echo request */
605 FD_SET(IcmpSock
, &Fds
);
606 Timeval
.tv_sec
= Timeout
/ 1000;
607 Timeval
.tv_usec
= Timeout
% 1000;
608 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
609 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
613 printf("Sending packet\n");
614 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
618 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
619 0, (SOCKADDR
*)&Target
, sizeof(Target
));
620 QueryTime(&SentTime
);
623 if (Status
== SOCKET_ERROR
)
625 if (WSAGetLastError() == WSAEHOSTUNREACH
)
626 FormatOutput(IDS_DEST_UNREACHABLE
);
628 FormatOutput(IDS_COULD_NOT_TRANSMIT
, WSAGetLastError());
633 /* Expect to receive ICMP echo reply */
635 FD_SET(IcmpSock
, &Fds
);
636 Timeval
.tv_sec
= Timeout
/ 1000;
637 Timeval
.tv_usec
= Timeout
% 1000;
640 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
641 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
643 Length
= sizeof(From
);
644 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
647 printf("Received packet\n");
648 DisplayBuffer(Buffer
, Status
);
654 if (Status
== SOCKET_ERROR
)
656 if (WSAGetLastError() != WSAETIMEDOUT
)
658 FormatOutput(IDS_COULD_NOT_RECV
, WSAGetLastError());
667 FormatOutput(IDS_REQUEST_TIMEOUT
);
672 } while (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
));
679 /* Program entry point */
680 int wmain(int argc
, LPWSTR argv
[])
687 hStdOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
691 if ((ParseCmdline(argc
, argv
)) && (Setup()))
694 FormatOutput(IDS_PING_WITH_BYTES
, TargetName
, TargetIP
, DataSize
);
697 while ((NeverStop
) || (Count
< PingCount
))
701 if((NeverStop
) || (Count
< PingCount
))
707 /* Calculate avarage round trip time */
708 if ((SentCount
- LostCount
) > 0)
709 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
713 /* Calculate loss percent */
714 Count
= SentCount
? (LostCount
* 100) / SentCount
: 0;
719 TimeToMsString(MinTime
, MinRTT
);
720 TimeToMsString(MaxTime
, MaxRTT
);
721 TimeToMsString(AvgTime
, AvgRTT
);
723 /* Print statistics */
724 FormatOutput(IDS_PING_STATISTICS
, TargetIP
);
725 FormatOutput(IDS_PACKETS_SENT_RECEIVED_LOST
,\
726 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
729 /* Print approximate times or NO approximate times if 100% loss */
730 if ((SentCount
- LostCount
) > 0)
732 FormatOutput(IDS_APPROXIMATE_ROUND_TRIP
);
733 FormatOutput(IDS_MIN_MAX_AVERAGE
, MinTime
, MaxTime
, AvgTime
);