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);
117 void FormatOutput(UINT uID
, ...)
124 PCHAR pAnsiBuf
= AnsiBuf
;
130 va_start(valist
, uID
);
132 if (!LoadString(GetModuleHandle(NULL
), uID
,
133 Format
, sizeof(Format
) / sizeof(WCHAR
)))
138 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
, Format
, 0, 0, Buf
,\
139 sizeof(Buf
) / sizeof(WCHAR
), &valist
);
143 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
146 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
|\
147 FORMAT_MESSAGE_ALLOCATE_BUFFER
,\
148 Format
, 0, 0, (LPWSTR
)&pBuf
, 0, &valist
);
154 if(GetFileType(hStdOutput
) == FILE_TYPE_CHAR
)
156 /* Is a console or a printer */
157 WriteConsole(hStdOutput
, pBuf
, DataLength
, &written
, NULL
);
161 /* Is a pipe, socket, file or other */
162 AnsiLength
= WideCharToMultiByte(CP_ACP
, 0, pBuf
, DataLength
,\
163 NULL
, 0, NULL
, NULL
);
165 if(AnsiLength
>= sizeof(AnsiBuf
))
166 pAnsiBuf
= (PCHAR
)HeapAlloc(GetProcessHeap(), 0, AnsiLength
);
168 AnsiLength
= WideCharToMultiByte(CP_OEMCP
, 0, pBuf
, DataLength
,\
169 pAnsiBuf
, AnsiLength
, " ", NULL
);
171 WriteFile(hStdOutput
, pAnsiBuf
, AnsiLength
, &written
, NULL
);
173 if(pAnsiBuf
!= AnsiBuf
)
174 HeapFree(NULL
, 0, pAnsiBuf
);
181 /* Display usage information on screen */
182 static VOID
Usage(VOID
)
184 FormatOutput(IDS_USAGE
);
187 /* Reset configuration to default values */
188 static VOID
Reset(VOID
)
190 LARGE_INTEGER PerformanceCounterFrequency
;
193 ResolveAddresses
= FALSE
;
196 DontFragment
= FALSE
;
200 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
202 if (UsePerformanceCounter
)
204 /* Performance counters may return incorrect results on some multiprocessor
205 platforms so we restrict execution on the first processor. This may fail
206 on Windows NT so we fall back to GetCurrentTick() for timing */
207 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
208 UsePerformanceCounter
= FALSE
;
210 /* Convert frequency to ticks per millisecond */
211 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
212 /* And to ticks per microsecond */
213 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
215 if (!UsePerformanceCounter
)
217 /* 1 tick per millisecond for GetCurrentTick() */
218 TicksPerMs
.QuadPart
= 1;
219 /* GetCurrentTick() cannot handle microseconds */
220 TicksPerUs
.QuadPart
= 1;
224 /* Parse command line parameters */
225 static BOOL
ParseCmdline(int argc
, LPWSTR argv
[])
228 BOOL FoundTarget
= FALSE
, InvalidOption
= FALSE
;
236 for (i
= 1; i
< argc
; i
++)
238 if (argv
[i
][0] == L
'-' || argv
[i
][0] == L
'/')
242 case L
't': NeverStop
= TRUE
; break;
243 case L
'a': ResolveAddresses
= TRUE
; break;
246 PingCount
= wcstoul(argv
[++i
], NULL
, 0);
248 InvalidOption
= TRUE
;
253 DataSize
= wcstoul(argv
[++i
], NULL
, 0);
255 if (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
) - sizeof(IPv4_HEADER
))
257 FormatOutput(IDS_BAD_VALUE_OPTION_L
, ICMP_MAXSIZE
- \
258 (int)sizeof(ICMP_ECHO_PACKET
) - \
259 (int)sizeof(IPv4_HEADER
));
263 InvalidOption
= TRUE
;
265 case L
'f': DontFragment
= TRUE
; break;
268 TTLValue
= wcstoul(argv
[++i
], NULL
, 0);
270 InvalidOption
= TRUE
;
274 TOSValue
= wcstoul(argv
[++i
], NULL
, 0);
276 InvalidOption
= TRUE
;
280 Timeout
= wcstoul(argv
[++i
], NULL
, 0);
282 InvalidOption
= TRUE
;
288 FormatOutput(IDS_BAD_OPTION
, argv
[i
]);
293 FormatOutput(IDS_BAD_OPTION_FORMAT
, argv
[i
]);
301 FormatOutput(IDS_BAD_PARAMETER
, argv
[i
]);
306 wcscpy(TargetName
, argv
[i
]);
314 FormatOutput(IDS_DEST_MUST_BE_SPECIFIED
);
321 /* Calculate checksum of data */
322 static WORD
Checksum(PUSHORT data
, UINT size
)
329 size
-= sizeof(USHORT
);
333 sum
+= *(UCHAR
*)data
;
335 sum
= (sum
>> 16) + (sum
& 0xFFFF);
338 return (USHORT
)(~sum
);
341 /* Prepare to ping target */
342 static BOOL
Setup(VOID
)
344 WORD wVersionRequested
;
349 CHAR aTargetName
[256];
351 wVersionRequested
= MAKEWORD(2, 2);
353 Status
= WSAStartup(wVersionRequested
, &WsaData
);
356 FormatOutput(IDS_COULD_NOT_INIT_WINSOCK
);
360 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
361 if (IcmpSock
== INVALID_SOCKET
)
363 FormatOutput(IDS_COULD_NOT_CREATE_SOCKET
, WSAGetLastError());
367 if (setsockopt(IcmpSock
,
370 (const char *)&DontFragment
,
371 sizeof(DontFragment
)) == SOCKET_ERROR
)
373 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
377 if (setsockopt(IcmpSock
,
380 (const char *)&TTLValue
,
381 sizeof(TTLValue
)) == SOCKET_ERROR
)
383 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
388 if(!WideCharToMultiByte(CP_ACP
, 0, TargetName
, -1, aTargetName
,\
389 sizeof(aTargetName
), NULL
, NULL
))
391 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
395 ZeroMemory(&Target
, sizeof(Target
));
397 Addr
= inet_addr(aTargetName
);
398 if (Addr
== INADDR_NONE
)
400 phe
= gethostbyname(aTargetName
);
403 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
407 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
408 Target
.sin_family
= phe
->h_addrtype
;
412 Target
.sin_addr
.s_addr
= Addr
;
413 Target
.sin_family
= AF_INET
;
417 swprintf(TargetIP
, L
"%d.%d.%d.%d", Target
.sin_addr
.S_un
.S_un_b
.s_b1
,\
418 Target
.sin_addr
.S_un
.S_un_b
.s_b2
,\
419 Target
.sin_addr
.S_un
.S_un_b
.s_b3
,\
420 Target
.sin_addr
.S_un
.S_un_b
.s_b4
);
432 static VOID
Cleanup(VOID
)
434 if (IcmpSock
!= INVALID_SOCKET
)
435 closesocket(IcmpSock
);
440 static VOID
QueryTime(PLARGE_INTEGER Time
)
442 if (UsePerformanceCounter
)
444 if (QueryPerformanceCounter(Time
) == 0)
446 /* This should not happen, but we fall
447 back to GetCurrentTick() if it does */
448 Time
->u
.LowPart
= (ULONG
)GetTickCount();
449 Time
->u
.HighPart
= 0;
451 /* 1 tick per millisecond for GetCurrentTick() */
452 TicksPerMs
.QuadPart
= 1;
453 /* GetCurrentTick() cannot handle microseconds */
454 TicksPerUs
.QuadPart
= 1;
456 UsePerformanceCounter
= FALSE
;
461 Time
->u
.LowPart
= (ULONG
)GetTickCount();
462 Time
->u
.HighPart
= 0;
466 static VOID
TimeToMsString(LPWSTR String
, ULONG Length
, LARGE_INTEGER Time
)
469 LARGE_INTEGER LargeTime
;
472 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
474 _i64tow(LargeTime
.QuadPart
, Convstr
, 10);
475 wcscpy(String
, Convstr
);
476 ms
= String
+ wcslen(String
);
477 LoadString(GetModuleHandle(NULL
), IDS_MS
, ms
, Length
- (ms
- String
));
480 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
482 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
484 PIPv4_HEADER IpHeader
;
485 PICMP_ECHO_PACKET Icmp
;
488 LARGE_INTEGER RelativeTime
;
489 LARGE_INTEGER LargeTime
;
493 IpHeader
= (PIPv4_HEADER
)buffer
;
495 IphLength
= IpHeader
->IHL
* 4;
497 if (size
< IphLength
+ ICMP_MINSIZE
)
500 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
505 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
507 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
510 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
515 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
518 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
523 if (from
->sin_addr
.s_addr
!= Target
.sin_addr
.s_addr
)
526 printf("Bad source address (%s should be %s)\n", inet_ntoa(from
->sin_addr
), inet_ntoa(Target
.sin_addr
));
531 QueryTime(&LargeTime
);
533 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- SentTime
.QuadPart
);
535 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
538 LoadString(GetModuleHandle(NULL
), IDS_1MS
, Time
, sizeof(Time
) / sizeof(WCHAR
));
543 TimeToMsString(Time
, sizeof(Time
) / sizeof(WCHAR
), 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
, sizeof(MinTime
) / sizeof(WCHAR
), MinRTT
);
720 TimeToMsString(MaxTime
, sizeof(MaxTime
) / sizeof(WCHAR
), MaxRTT
);
721 TimeToMsString(AvgTime
, sizeof(AvgTime
) / sizeof(WCHAR
), 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
);