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 /* General ICMP constants */
20 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
21 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
23 /* ICMP message types */
24 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
25 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
29 /* IPv4 header structure */
30 typedef struct _IPv4_HEADER
33 unsigned char Version
:4;
35 unsigned short Length
;
37 unsigned short FragFlags
;
39 unsigned char Protocol
;
40 unsigned short Checksum
;
41 unsigned int SrcAddress
;
42 unsigned int DstAddress
;
43 } IPv4_HEADER
, *PIPv4_HEADER
;
45 /* ICMP echo request/reply header structure */
46 typedef struct _ICMP_HEADER
50 unsigned short Checksum
;
52 unsigned short SeqNum
;
53 } ICMP_HEADER
, *PICMP_HEADER
;
55 typedef struct _ICMP_ECHO_PACKET
58 } ICMP_ECHO_PACKET
, *PICMP_ECHO_PACKET
;
64 BOOL ResolveAddresses
;
66 UINT DataSize
; /* ICMP echo request data size */
71 WCHAR TargetName
[256];
81 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
85 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
86 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
87 LARGE_INTEGER SentTime
;
88 BOOL UsePerformanceCounter
;
92 /* Display the contents of a buffer */
93 static VOID
DisplayBuffer(
100 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
103 for (i
= 0; i
< Size
; i
++)
107 printf("%02X ", (p
[i
]) & 0xFF);
113 MyLoadString(UINT uID
)
119 hres
= FindResourceW(NULL
, MAKEINTRESOURCEW((LOWORD(uID
) >> 4) + 1), RT_STRING
);
120 if (!hres
) return NULL
;
122 hResData
= LoadResource(NULL
, hres
);
123 if (!hResData
) return NULL
;
125 pwsz
= LockResource(hResData
);
126 if (!pwsz
) return NULL
;
130 void FormatOutput(UINT uID
, ...)
140 va_start(valist
, uID
);
142 Format
= MyLoadString(uID
);
145 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
, Format
, 0, 0, Buf
,\
146 sizeof(Buf
) / sizeof(WCHAR
), &valist
);
150 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
153 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
|\
154 FORMAT_MESSAGE_ALLOCATE_BUFFER
,\
155 Format
, 0, 0, (LPWSTR
)&pBuf
, 0, &valist
);
161 WriteConsole(hStdOutput
, pBuf
, DataLength
, &written
, NULL
);
167 /* Display usage information on screen */
168 static VOID
Usage(VOID
)
170 FormatOutput(IDS_USAGE
);
173 /* Reset configuration to default values */
174 static VOID
Reset(VOID
)
176 LARGE_INTEGER PerformanceCounterFrequency
;
179 ResolveAddresses
= FALSE
;
182 DontFragment
= FALSE
;
186 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
188 if (UsePerformanceCounter
)
190 /* Performance counters may return incorrect results on some multiprocessor
191 platforms so we restrict execution on the first processor. This may fail
192 on Windows NT so we fall back to GetCurrentTick() for timing */
193 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
194 UsePerformanceCounter
= FALSE
;
196 /* Convert frequency to ticks per millisecond */
197 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
198 /* And to ticks per microsecond */
199 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
201 if (!UsePerformanceCounter
)
203 /* 1 tick per millisecond for GetCurrentTick() */
204 TicksPerMs
.QuadPart
= 1;
205 /* GetCurrentTick() cannot handle microseconds */
206 TicksPerUs
.QuadPart
= 1;
210 /* Return ULONG in a string */
211 static ULONG
GetULONG(LPWSTR String
)
217 Length
= (UINT
)wcslen(String
);
218 while ((i
< Length
) && ((String
[i
] < L
'0') || (String
[i
] > L
'9'))) i
++;
219 if ((i
>= Length
) || ((String
[i
] < L
'0') || (String
[i
] > L
'9')))
221 InvalidOption
= TRUE
;
224 Value
= wcstoul(&String
[i
], &StopString
, 10);
229 /* Return ULONG in a string. Try next paramter if not successful */
230 static ULONG
GetULONG2(LPWSTR String1
, LPWSTR String2
, PINT i
)
234 Value
= GetULONG(String1
);
237 InvalidOption
= FALSE
;
238 if (String2
[0] != L
'-')
240 Value
= GetULONG(String2
);
249 /* Parse command line parameters */
250 static BOOL
ParseCmdline(int argc
, LPWSTR argv
[])
260 InvalidOption
= FALSE
;
262 for (i
= 1; i
< argc
; i
++)
264 if (argv
[i
][0] == L
'-')
268 case L
't': NeverStop
= TRUE
; break;
269 case L
'a': ResolveAddresses
= TRUE
; break;
270 case L
'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
272 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
273 if (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
) - sizeof(IPv4_HEADER
))
275 FormatOutput(IDS_BAD_VALUE_OPTION_L
, ICMP_MAXSIZE
- \
276 (int)sizeof(ICMP_ECHO_PACKET
) - \
277 (int)sizeof(IPv4_HEADER
));
281 case L
'f': DontFragment
= TRUE
; break;
282 case L
'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
283 case L
'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
284 case L
'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
286 FormatOutput(IDS_BAD_OPTION
, argv
[i
]);
292 FormatOutput(IDS_BAD_OPTION_FORMAT
, argv
[i
]);
300 FormatOutput(IDS_BAD_PARAMETER
, argv
[i
]);
305 wcscpy(TargetName
, argv
[i
]);
311 if ((!ShowUsage
) && (!FoundTarget
))
313 FormatOutput(IDS_DEST_MUST_BE_SPECIFIED
);
325 /* Calculate checksum of data */
326 static WORD
Checksum(PUSHORT data
, UINT size
)
333 size
-= sizeof(USHORT
);
337 sum
+= *(UCHAR
*)data
;
339 sum
= (sum
>> 16) + (sum
& 0xFFFF);
342 return (USHORT
)(~sum
);
345 /* Prepare to ping target */
346 static BOOL
Setup(VOID
)
348 WORD wVersionRequested
;
353 CHAR aTargetName
[256];
355 wVersionRequested
= MAKEWORD(2, 2);
357 Status
= WSAStartup(wVersionRequested
, &WsaData
);
360 FormatOutput(IDS_COULD_NOT_INIT_WINSOCK
);
364 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
365 if (IcmpSock
== INVALID_SOCKET
)
367 FormatOutput(IDS_COULD_NOT_CREATE_SOCKET
, WSAGetLastError());
371 if (setsockopt(IcmpSock
,
374 (const char *)&DontFragment
,
375 sizeof(DontFragment
)) == SOCKET_ERROR
)
377 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
381 if (setsockopt(IcmpSock
,
384 (const char *)&TTLValue
,
385 sizeof(TTLValue
)) == SOCKET_ERROR
)
387 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
392 if(!WideCharToMultiByte(CP_ACP
, 0, TargetName
, -1, aTargetName
,\
393 sizeof(aTargetName
), NULL
, NULL
))
395 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
399 ZeroMemory(&Target
, sizeof(Target
));
401 Addr
= inet_addr(aTargetName
);
402 if (Addr
== INADDR_NONE
)
404 phe
= gethostbyname(aTargetName
);
407 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
411 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
412 Target
.sin_family
= phe
->h_addrtype
;
416 Target
.sin_addr
.s_addr
= Addr
;
417 Target
.sin_family
= AF_INET
;
421 swprintf(TargetIP
, L
"%d.%d.%d.%d", Target
.sin_addr
.S_un
.S_un_b
.s_b1
,\
422 Target
.sin_addr
.S_un
.S_un_b
.s_b2
,\
423 Target
.sin_addr
.S_un
.S_un_b
.s_b3
,\
424 Target
.sin_addr
.S_un
.S_un_b
.s_b4
);
436 static VOID
Cleanup(VOID
)
438 if (IcmpSock
!= INVALID_SOCKET
)
439 closesocket(IcmpSock
);
444 static VOID
QueryTime(PLARGE_INTEGER Time
)
446 if (UsePerformanceCounter
)
448 if (QueryPerformanceCounter(Time
) == 0)
450 /* This should not happen, but we fall
451 back to GetCurrentTick() if it does */
452 Time
->u
.LowPart
= (ULONG
)GetTickCount();
453 Time
->u
.HighPart
= 0;
455 /* 1 tick per millisecond for GetCurrentTick() */
456 TicksPerMs
.QuadPart
= 1;
457 /* GetCurrentTick() cannot handle microseconds */
458 TicksPerUs
.QuadPart
= 1;
460 UsePerformanceCounter
= FALSE
;
465 Time
->u
.LowPart
= (ULONG
)GetTickCount();
466 Time
->u
.HighPart
= 0;
470 static VOID
TimeToMsString(LPWSTR String
, LARGE_INTEGER Time
)
473 LARGE_INTEGER LargeTime
;
476 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
478 _i64tow(LargeTime
.QuadPart
, Convstr
, 10);
479 wcscpy(String
, Convstr
);
480 ms
= MyLoadString(IDS_MS
);
484 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
486 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
488 PIPv4_HEADER IpHeader
;
489 PICMP_ECHO_PACKET Icmp
;
492 LARGE_INTEGER RelativeTime
;
493 LARGE_INTEGER LargeTime
;
497 IpHeader
= (PIPv4_HEADER
)buffer
;
499 IphLength
= IpHeader
->IHL
* 4;
501 if (size
< IphLength
+ ICMP_MINSIZE
)
504 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
509 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
511 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
514 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
519 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
522 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
527 if (from
->sin_addr
.s_addr
!= Target
.sin_addr
.s_addr
)
530 printf("Bad source address (%s should be %s)\n", inet_ntoa(from
->sin_addr
), inet_ntoa(Target
.sin_addr
));
535 QueryTime(&LargeTime
);
537 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- SentTime
.QuadPart
);
539 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
544 ms1
= MyLoadString(IDS_1MS
);
550 TimeToMsString(Time
, RelativeTime
);
554 swprintf(wfromIP
, L
"%d.%d.%d.%d", from
->sin_addr
.S_un
.S_un_b
.s_b1
,\
555 from
->sin_addr
.S_un
.S_un_b
.s_b2
,\
556 from
->sin_addr
.S_un
.S_un_b
.s_b3
,\
557 from
->sin_addr
.S_un
.S_un_b
.s_b4
);
558 FormatOutput(IDS_REPLY_FROM
, wfromIP
,\
559 size
- IphLength
- (int)sizeof(ICMP_ECHO_PACKET
),\
560 Sign
, Time
, IpHeader
->TTL
);
562 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
)
564 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
567 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
568 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
570 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
575 /* Send and receive one ping */
576 static BOOL
Ping(VOID
)
583 PICMP_ECHO_PACKET Packet
;
585 /* Account for extra space for IP header when packet is received */
586 Size
= DataSize
+ 128;
587 Buffer
= GlobalAlloc(0, Size
);
590 FormatOutput(IDS_NOT_ENOUGH_RESOURCES
);
594 ZeroMemory(Buffer
, Size
);
595 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
597 /* Assemble ICMP echo request packet */
598 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
599 Packet
->Icmp
.Code
= 0;
600 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
601 Packet
->Icmp
.SeqNum
= htons((USHORT
)CurrentSeqNum
);
602 Packet
->Icmp
.Checksum
= 0;
604 /* Calculate checksum for ICMP header and data area */
605 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
609 /* Send ICMP echo request */
612 FD_SET(IcmpSock
, &Fds
);
613 Timeval
.tv_sec
= Timeout
/ 1000;
614 Timeval
.tv_usec
= Timeout
% 1000;
615 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
616 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
620 printf("Sending packet\n");
621 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
625 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
626 0, (SOCKADDR
*)&Target
, sizeof(Target
));
627 QueryTime(&SentTime
);
630 if (Status
== SOCKET_ERROR
)
632 if (WSAGetLastError() == WSAEHOSTUNREACH
)
633 FormatOutput(IDS_DEST_UNREACHABLE
);
635 FormatOutput(IDS_COULD_NOT_TRANSMIT
, WSAGetLastError());
640 /* Expect to receive ICMP echo reply */
642 FD_SET(IcmpSock
, &Fds
);
643 Timeval
.tv_sec
= Timeout
/ 1000;
644 Timeval
.tv_usec
= Timeout
% 1000;
647 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
648 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
650 Length
= sizeof(From
);
651 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
654 printf("Received packet\n");
655 DisplayBuffer(Buffer
, Status
);
661 if (Status
== SOCKET_ERROR
)
663 if (WSAGetLastError() != WSAETIMEDOUT
)
665 FormatOutput(IDS_COULD_NOT_RECV
, WSAGetLastError());
674 FormatOutput(IDS_REQUEST_TIMEOUT
);
679 } while (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
));
686 /* Program entry point */
687 int wmain(int argc
, LPWSTR argv
[])
694 hStdOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
698 if ((ParseCmdline(argc
, argv
)) && (Setup()))
701 FormatOutput(IDS_PING_WITH_BYTES
, TargetName
, TargetIP
, DataSize
);
704 while ((NeverStop
) || (Count
< PingCount
))
708 if((NeverStop
) || (Count
< PingCount
))
714 /* Calculate avarage round trip time */
715 if ((SentCount
- LostCount
) > 0)
716 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
720 /* Calculate loss percent */
721 Count
= SentCount
? (LostCount
* 100) / SentCount
: 0;
726 TimeToMsString(MinTime
, MinRTT
);
727 TimeToMsString(MaxTime
, MaxRTT
);
728 TimeToMsString(AvgTime
, AvgRTT
);
730 /* Print statistics */
731 FormatOutput(IDS_PING_STATISTICS
, TargetIP
);
732 FormatOutput(IDS_PACKETS_SENT_RECEIVED_LOST
,\
733 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
735 /* Print approximate times or NO approximate times if 100% loss */
736 if ((SentCount
- LostCount
) > 0)
738 FormatOutput(IDS_APPROXIMATE_ROUND_TRIP
);
739 FormatOutput(IDS_MIN_MAX_AVERAGE
, MinTime
, MaxTime
, AvgTime
);