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
)
120 hres
= FindResourceW(NULL
, MAKEINTRESOURCEW((LOWORD(uID
) >> 4) + 1), RT_STRING
);
121 if (!hres
) return NULL
;
123 hResData
= LoadResource(NULL
, hres
);
124 if (!hResData
) return NULL
;
126 pwsz
= LockResource(hResData
);
127 if (!pwsz
) return NULL
;
129 string_num
= uID
& 15;
130 for (i
= 0; i
< string_num
; i
++)
136 void FormatOutput(UINT uID
, ...)
146 va_start(valist
, uID
);
148 Format
= MyLoadString(uID
);
151 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
, Format
, 0, 0, Buf
,\
152 sizeof(Buf
) / sizeof(WCHAR
), &valist
);
156 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
159 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
|\
160 FORMAT_MESSAGE_ALLOCATE_BUFFER
,\
161 Format
, 0, 0, (LPWSTR
)&pBuf
, 0, &valist
);
167 WriteConsole(hStdOutput
, pBuf
, DataLength
, &written
, NULL
);
173 /* Display usage information on screen */
174 static VOID
Usage(VOID
)
176 FormatOutput(IDS_USAGE
);
179 /* Reset configuration to default values */
180 static VOID
Reset(VOID
)
182 LARGE_INTEGER PerformanceCounterFrequency
;
185 ResolveAddresses
= FALSE
;
188 DontFragment
= FALSE
;
192 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
194 if (UsePerformanceCounter
)
196 /* Performance counters may return incorrect results on some multiprocessor
197 platforms so we restrict execution on the first processor. This may fail
198 on Windows NT so we fall back to GetCurrentTick() for timing */
199 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
200 UsePerformanceCounter
= FALSE
;
202 /* Convert frequency to ticks per millisecond */
203 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
204 /* And to ticks per microsecond */
205 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
207 if (!UsePerformanceCounter
)
209 /* 1 tick per millisecond for GetCurrentTick() */
210 TicksPerMs
.QuadPart
= 1;
211 /* GetCurrentTick() cannot handle microseconds */
212 TicksPerUs
.QuadPart
= 1;
216 /* Return ULONG in a string */
217 static ULONG
GetULONG(LPWSTR String
)
223 Length
= (UINT
)wcslen(String
);
224 while ((i
< Length
) && ((String
[i
] < L
'0') || (String
[i
] > L
'9'))) i
++;
225 if ((i
>= Length
) || ((String
[i
] < L
'0') || (String
[i
] > L
'9')))
227 InvalidOption
= TRUE
;
230 Value
= wcstoul(&String
[i
], &StopString
, 10);
235 /* Return ULONG in a string. Try next paramter if not successful */
236 static ULONG
GetULONG2(LPWSTR String1
, LPWSTR String2
, PINT i
)
240 Value
= GetULONG(String1
);
243 InvalidOption
= FALSE
;
244 if (String2
[0] != L
'-')
246 Value
= GetULONG(String2
);
255 /* Parse command line parameters */
256 static BOOL
ParseCmdline(int argc
, LPWSTR argv
[])
266 InvalidOption
= FALSE
;
268 for (i
= 1; i
< argc
; i
++)
270 if (argv
[i
][0] == L
'-')
274 case L
't': NeverStop
= TRUE
; break;
275 case L
'a': ResolveAddresses
= TRUE
; break;
276 case L
'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
278 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
279 if (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
) - sizeof(IPv4_HEADER
))
281 FormatOutput(IDS_BAD_VALUE_OPTION_L
, ICMP_MAXSIZE
- \
282 (int)sizeof(ICMP_ECHO_PACKET
) - \
283 (int)sizeof(IPv4_HEADER
));
287 case L
'f': DontFragment
= TRUE
; break;
288 case L
'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
289 case L
'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
290 case L
'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
292 FormatOutput(IDS_BAD_OPTION
, argv
[i
]);
298 FormatOutput(IDS_BAD_OPTION_FORMAT
, argv
[i
]);
306 FormatOutput(IDS_BAD_PARAMETER
, argv
[i
]);
311 wcscpy(TargetName
, argv
[i
]);
317 if ((!ShowUsage
) && (!FoundTarget
))
319 FormatOutput(IDS_DEST_MUST_BE_SPECIFIED
);
331 /* Calculate checksum of data */
332 static WORD
Checksum(PUSHORT data
, UINT size
)
339 size
-= sizeof(USHORT
);
343 sum
+= *(UCHAR
*)data
;
345 sum
= (sum
>> 16) + (sum
& 0xFFFF);
348 return (USHORT
)(~sum
);
351 /* Prepare to ping target */
352 static BOOL
Setup(VOID
)
354 WORD wVersionRequested
;
359 CHAR aTargetName
[256];
361 wVersionRequested
= MAKEWORD(2, 2);
363 Status
= WSAStartup(wVersionRequested
, &WsaData
);
366 FormatOutput(IDS_COULD_NOT_INIT_WINSOCK
);
370 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
371 if (IcmpSock
== INVALID_SOCKET
)
373 FormatOutput(IDS_COULD_NOT_CREATE_SOCKET
, WSAGetLastError());
377 if (setsockopt(IcmpSock
,
380 (const char *)&DontFragment
,
381 sizeof(DontFragment
)) == SOCKET_ERROR
)
383 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
387 if (setsockopt(IcmpSock
,
390 (const char *)&TTLValue
,
391 sizeof(TTLValue
)) == SOCKET_ERROR
)
393 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
398 if(!WideCharToMultiByte(CP_ACP
, 0, TargetName
, -1, aTargetName
,\
399 sizeof(aTargetName
), NULL
, NULL
))
401 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
405 ZeroMemory(&Target
, sizeof(Target
));
407 Addr
= inet_addr(aTargetName
);
408 if (Addr
== INADDR_NONE
)
410 phe
= gethostbyname(aTargetName
);
413 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
417 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
418 Target
.sin_family
= phe
->h_addrtype
;
422 Target
.sin_addr
.s_addr
= Addr
;
423 Target
.sin_family
= AF_INET
;
427 swprintf(TargetIP
, L
"%d.%d.%d.%d", Target
.sin_addr
.S_un
.S_un_b
.s_b1
,\
428 Target
.sin_addr
.S_un
.S_un_b
.s_b2
,\
429 Target
.sin_addr
.S_un
.S_un_b
.s_b3
,\
430 Target
.sin_addr
.S_un
.S_un_b
.s_b4
);
442 static VOID
Cleanup(VOID
)
444 if (IcmpSock
!= INVALID_SOCKET
)
445 closesocket(IcmpSock
);
450 static VOID
QueryTime(PLARGE_INTEGER Time
)
452 if (UsePerformanceCounter
)
454 if (QueryPerformanceCounter(Time
) == 0)
456 /* This should not happen, but we fall
457 back to GetCurrentTick() if it does */
458 Time
->u
.LowPart
= (ULONG
)GetTickCount();
459 Time
->u
.HighPart
= 0;
461 /* 1 tick per millisecond for GetCurrentTick() */
462 TicksPerMs
.QuadPart
= 1;
463 /* GetCurrentTick() cannot handle microseconds */
464 TicksPerUs
.QuadPart
= 1;
466 UsePerformanceCounter
= FALSE
;
471 Time
->u
.LowPart
= (ULONG
)GetTickCount();
472 Time
->u
.HighPart
= 0;
476 static VOID
TimeToMsString(LPWSTR String
, LARGE_INTEGER Time
)
479 LARGE_INTEGER LargeTime
;
482 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
484 _i64tow(LargeTime
.QuadPart
, Convstr
, 10);
485 wcscpy(String
, Convstr
);
486 ms
= MyLoadString(IDS_MS
);
490 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
492 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
494 PIPv4_HEADER IpHeader
;
495 PICMP_ECHO_PACKET Icmp
;
498 LARGE_INTEGER RelativeTime
;
499 LARGE_INTEGER LargeTime
;
503 IpHeader
= (PIPv4_HEADER
)buffer
;
505 IphLength
= IpHeader
->IHL
* 4;
507 if (size
< IphLength
+ ICMP_MINSIZE
)
510 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
515 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
517 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
520 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
525 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
528 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
533 if (from
->sin_addr
.s_addr
!= Target
.sin_addr
.s_addr
)
536 printf("Bad source address (%s should be %s)\n", inet_ntoa(from
->sin_addr
), inet_ntoa(Target
.sin_addr
));
541 QueryTime(&LargeTime
);
543 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- SentTime
.QuadPart
);
545 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
550 ms1
= MyLoadString(IDS_1MS
);
556 TimeToMsString(Time
, RelativeTime
);
560 swprintf(wfromIP
, L
"%d.%d.%d.%d", from
->sin_addr
.S_un
.S_un_b
.s_b1
,\
561 from
->sin_addr
.S_un
.S_un_b
.s_b2
,\
562 from
->sin_addr
.S_un
.S_un_b
.s_b3
,\
563 from
->sin_addr
.S_un
.S_un_b
.s_b4
);
564 FormatOutput(IDS_REPLY_FROM
, wfromIP
,\
565 size
- IphLength
- (int)sizeof(ICMP_ECHO_PACKET
),\
566 Sign
, Time
, IpHeader
->TTL
);
568 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
)
570 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
573 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
574 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
576 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
581 /* Send and receive one ping */
582 static BOOL
Ping(VOID
)
589 PICMP_ECHO_PACKET Packet
;
591 /* Account for extra space for IP header when packet is received */
592 Size
= DataSize
+ 128;
593 Buffer
= GlobalAlloc(0, Size
);
596 FormatOutput(IDS_NOT_ENOUGH_RESOURCES
);
600 ZeroMemory(Buffer
, Size
);
601 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
603 /* Assemble ICMP echo request packet */
604 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
605 Packet
->Icmp
.Code
= 0;
606 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
607 Packet
->Icmp
.SeqNum
= htons((USHORT
)CurrentSeqNum
);
608 Packet
->Icmp
.Checksum
= 0;
610 /* Calculate checksum for ICMP header and data area */
611 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
615 /* Send ICMP echo request */
618 FD_SET(IcmpSock
, &Fds
);
619 Timeval
.tv_sec
= Timeout
/ 1000;
620 Timeval
.tv_usec
= Timeout
% 1000;
621 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
622 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
626 printf("Sending packet\n");
627 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
631 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
632 0, (SOCKADDR
*)&Target
, sizeof(Target
));
633 QueryTime(&SentTime
);
636 if (Status
== SOCKET_ERROR
)
638 if (WSAGetLastError() == WSAEHOSTUNREACH
)
639 FormatOutput(IDS_DEST_UNREACHABLE
);
641 FormatOutput(IDS_COULD_NOT_TRANSMIT
, WSAGetLastError());
646 /* Expect to receive ICMP echo reply */
648 FD_SET(IcmpSock
, &Fds
);
649 Timeval
.tv_sec
= Timeout
/ 1000;
650 Timeval
.tv_usec
= Timeout
% 1000;
653 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
654 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
656 Length
= sizeof(From
);
657 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
660 printf("Received packet\n");
661 DisplayBuffer(Buffer
, Status
);
667 if (Status
== SOCKET_ERROR
)
669 if (WSAGetLastError() != WSAETIMEDOUT
)
671 FormatOutput(IDS_COULD_NOT_RECV
, WSAGetLastError());
680 FormatOutput(IDS_REQUEST_TIMEOUT
);
685 } while (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
));
692 /* Program entry point */
693 int wmain(int argc
, LPWSTR argv
[])
700 hStdOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
704 if ((ParseCmdline(argc
, argv
)) && (Setup()))
707 FormatOutput(IDS_PING_WITH_BYTES
, TargetName
, TargetIP
, DataSize
);
710 while ((NeverStop
) || (Count
< PingCount
))
714 if((NeverStop
) || (Count
< PingCount
))
720 /* Calculate avarage round trip time */
721 if ((SentCount
- LostCount
) > 0)
722 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
726 /* Calculate loss percent */
727 Count
= SentCount
? (LostCount
* 100) / SentCount
: 0;
732 TimeToMsString(MinTime
, MinRTT
);
733 TimeToMsString(MaxTime
, MaxRTT
);
734 TimeToMsString(AvgTime
, AvgRTT
);
736 /* Print statistics */
737 FormatOutput(IDS_PING_STATISTICS
, TargetIP
);
738 FormatOutput(IDS_PACKETS_SENT_RECEIVED_LOST
,\
739 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
742 /* Print approximate times or NO approximate times if 100% loss */
743 if ((SentCount
- LostCount
) > 0)
745 FormatOutput(IDS_APPROXIMATE_ROUND_TRIP
);
746 FormatOutput(IDS_MIN_MAX_AVERAGE
, MinTime
, MaxTime
, AvgTime
);