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
;
63 BOOL ResolveAddresses
;
65 UINT DataSize
; /* ICMP echo request data size */
70 WCHAR TargetName
[256];
80 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
84 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
85 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
86 LARGE_INTEGER SentTime
;
87 BOOL UsePerformanceCounter
;
91 /* Display the contents of a buffer */
92 static VOID
DisplayBuffer(
99 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
102 for (i
= 0; i
< Size
; i
++)
106 printf("%02X ", (p
[i
]) & 0xFF);
112 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
;
128 string_num
= uID
& 15;
129 for (i
= 0; i
< string_num
; i
++)
135 void FormatOutput(UINT uID
, ...)
145 va_start(valist
, uID
);
147 Format
= MyLoadString(uID
);
150 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
, Format
, 0, 0, Buf
,\
151 sizeof(Buf
) / sizeof(WCHAR
), &valist
);
155 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
158 DataLength
= FormatMessage(FORMAT_MESSAGE_FROM_STRING
|\
159 FORMAT_MESSAGE_ALLOCATE_BUFFER
,\
160 Format
, 0, 0, (LPWSTR
)&pBuf
, 0, &valist
);
166 WriteConsole(hStdOutput
, pBuf
, DataLength
, &written
, NULL
);
172 /* Display usage information on screen */
173 static VOID
Usage(VOID
)
175 FormatOutput(IDS_USAGE
);
178 /* Reset configuration to default values */
179 static VOID
Reset(VOID
)
181 LARGE_INTEGER PerformanceCounterFrequency
;
184 ResolveAddresses
= FALSE
;
187 DontFragment
= FALSE
;
191 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
193 if (UsePerformanceCounter
)
195 /* Performance counters may return incorrect results on some multiprocessor
196 platforms so we restrict execution on the first processor. This may fail
197 on Windows NT so we fall back to GetCurrentTick() for timing */
198 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
199 UsePerformanceCounter
= FALSE
;
201 /* Convert frequency to ticks per millisecond */
202 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
203 /* And to ticks per microsecond */
204 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
206 if (!UsePerformanceCounter
)
208 /* 1 tick per millisecond for GetCurrentTick() */
209 TicksPerMs
.QuadPart
= 1;
210 /* GetCurrentTick() cannot handle microseconds */
211 TicksPerUs
.QuadPart
= 1;
215 /* Parse command line parameters */
216 static BOOL
ParseCmdline(int argc
, LPWSTR argv
[])
219 BOOL FoundTarget
= FALSE
, InvalidOption
= FALSE
;
227 for (i
= 1; i
< argc
; i
++)
229 if (argv
[i
][0] == L
'-' || argv
[i
][0] == L
'/')
233 case L
't': NeverStop
= TRUE
; break;
234 case L
'a': ResolveAddresses
= TRUE
; break;
237 PingCount
= wcstoul(argv
[++i
], NULL
, 0);
239 InvalidOption
= TRUE
;
244 DataSize
= wcstoul(argv
[++i
], NULL
, 0);
246 if (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
) - sizeof(IPv4_HEADER
))
248 FormatOutput(IDS_BAD_VALUE_OPTION_L
, ICMP_MAXSIZE
- \
249 (int)sizeof(ICMP_ECHO_PACKET
) - \
250 (int)sizeof(IPv4_HEADER
));
254 InvalidOption
= TRUE
;
256 case L
'f': DontFragment
= TRUE
; break;
259 TTLValue
= wcstoul(argv
[++i
], NULL
, 0);
261 InvalidOption
= TRUE
;
265 TOSValue
= wcstoul(argv
[++i
], NULL
, 0);
267 InvalidOption
= TRUE
;
271 Timeout
= wcstoul(argv
[++i
], NULL
, 0);
273 InvalidOption
= TRUE
;
279 FormatOutput(IDS_BAD_OPTION
, argv
[i
]);
284 FormatOutput(IDS_BAD_OPTION_FORMAT
, argv
[i
]);
292 FormatOutput(IDS_BAD_PARAMETER
, argv
[i
]);
297 wcscpy(TargetName
, argv
[i
]);
305 FormatOutput(IDS_DEST_MUST_BE_SPECIFIED
);
312 /* Calculate checksum of data */
313 static WORD
Checksum(PUSHORT data
, UINT size
)
320 size
-= sizeof(USHORT
);
324 sum
+= *(UCHAR
*)data
;
326 sum
= (sum
>> 16) + (sum
& 0xFFFF);
329 return (USHORT
)(~sum
);
332 /* Prepare to ping target */
333 static BOOL
Setup(VOID
)
335 WORD wVersionRequested
;
340 CHAR aTargetName
[256];
342 wVersionRequested
= MAKEWORD(2, 2);
344 Status
= WSAStartup(wVersionRequested
, &WsaData
);
347 FormatOutput(IDS_COULD_NOT_INIT_WINSOCK
);
351 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
352 if (IcmpSock
== INVALID_SOCKET
)
354 FormatOutput(IDS_COULD_NOT_CREATE_SOCKET
, WSAGetLastError());
358 if (setsockopt(IcmpSock
,
361 (const char *)&DontFragment
,
362 sizeof(DontFragment
)) == SOCKET_ERROR
)
364 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
368 if (setsockopt(IcmpSock
,
371 (const char *)&TTLValue
,
372 sizeof(TTLValue
)) == SOCKET_ERROR
)
374 FormatOutput(IDS_SETSOCKOPT_FAILED
, WSAGetLastError());
379 if(!WideCharToMultiByte(CP_ACP
, 0, TargetName
, -1, aTargetName
,\
380 sizeof(aTargetName
), NULL
, NULL
))
382 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
386 ZeroMemory(&Target
, sizeof(Target
));
388 Addr
= inet_addr(aTargetName
);
389 if (Addr
== INADDR_NONE
)
391 phe
= gethostbyname(aTargetName
);
394 FormatOutput(IDS_UNKNOWN_HOST
, TargetName
);
398 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
399 Target
.sin_family
= phe
->h_addrtype
;
403 Target
.sin_addr
.s_addr
= Addr
;
404 Target
.sin_family
= AF_INET
;
408 swprintf(TargetIP
, L
"%d.%d.%d.%d", Target
.sin_addr
.S_un
.S_un_b
.s_b1
,\
409 Target
.sin_addr
.S_un
.S_un_b
.s_b2
,\
410 Target
.sin_addr
.S_un
.S_un_b
.s_b3
,\
411 Target
.sin_addr
.S_un
.S_un_b
.s_b4
);
423 static VOID
Cleanup(VOID
)
425 if (IcmpSock
!= INVALID_SOCKET
)
426 closesocket(IcmpSock
);
431 static VOID
QueryTime(PLARGE_INTEGER Time
)
433 if (UsePerformanceCounter
)
435 if (QueryPerformanceCounter(Time
) == 0)
437 /* This should not happen, but we fall
438 back to GetCurrentTick() if it does */
439 Time
->u
.LowPart
= (ULONG
)GetTickCount();
440 Time
->u
.HighPart
= 0;
442 /* 1 tick per millisecond for GetCurrentTick() */
443 TicksPerMs
.QuadPart
= 1;
444 /* GetCurrentTick() cannot handle microseconds */
445 TicksPerUs
.QuadPart
= 1;
447 UsePerformanceCounter
= FALSE
;
452 Time
->u
.LowPart
= (ULONG
)GetTickCount();
453 Time
->u
.HighPart
= 0;
457 static VOID
TimeToMsString(LPWSTR String
, LARGE_INTEGER Time
)
460 LARGE_INTEGER LargeTime
;
463 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
465 _i64tow(LargeTime
.QuadPart
, Convstr
, 10);
466 wcscpy(String
, Convstr
);
467 ms
= MyLoadString(IDS_MS
);
471 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
473 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
475 PIPv4_HEADER IpHeader
;
476 PICMP_ECHO_PACKET Icmp
;
479 LARGE_INTEGER RelativeTime
;
480 LARGE_INTEGER LargeTime
;
484 IpHeader
= (PIPv4_HEADER
)buffer
;
486 IphLength
= IpHeader
->IHL
* 4;
488 if (size
< IphLength
+ ICMP_MINSIZE
)
491 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
496 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
498 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
)
501 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
506 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId())
509 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
514 if (from
->sin_addr
.s_addr
!= Target
.sin_addr
.s_addr
)
517 printf("Bad source address (%s should be %s)\n", inet_ntoa(from
->sin_addr
), inet_ntoa(Target
.sin_addr
));
522 QueryTime(&LargeTime
);
524 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- SentTime
.QuadPart
);
526 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1)
531 ms1
= MyLoadString(IDS_1MS
);
537 TimeToMsString(Time
, RelativeTime
);
541 swprintf(wfromIP
, L
"%d.%d.%d.%d", from
->sin_addr
.S_un
.S_un_b
.s_b1
,\
542 from
->sin_addr
.S_un
.S_un_b
.s_b2
,\
543 from
->sin_addr
.S_un
.S_un_b
.s_b3
,\
544 from
->sin_addr
.S_un
.S_un_b
.s_b4
);
545 FormatOutput(IDS_REPLY_FROM
, wfromIP
,\
546 size
- IphLength
- (int)sizeof(ICMP_ECHO_PACKET
),\
547 Sign
, Time
, IpHeader
->TTL
);
549 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
)
551 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
554 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
555 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
557 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
562 /* Send and receive one ping */
563 static BOOL
Ping(VOID
)
570 PICMP_ECHO_PACKET Packet
;
572 /* Account for extra space for IP header when packet is received */
573 Size
= DataSize
+ 128;
574 Buffer
= GlobalAlloc(0, Size
);
577 FormatOutput(IDS_NOT_ENOUGH_RESOURCES
);
581 ZeroMemory(Buffer
, Size
);
582 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
584 /* Assemble ICMP echo request packet */
585 Packet
->Icmp
.Type
= ICMPMSG_ECHOREQUEST
;
586 Packet
->Icmp
.Code
= 0;
587 Packet
->Icmp
.Id
= (USHORT
)GetCurrentProcessId();
588 Packet
->Icmp
.SeqNum
= htons((USHORT
)CurrentSeqNum
);
589 Packet
->Icmp
.Checksum
= 0;
591 /* Calculate checksum for ICMP header and data area */
592 Packet
->Icmp
.Checksum
= Checksum((PUSHORT
)&Packet
->Icmp
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
596 /* Send ICMP echo request */
599 FD_SET(IcmpSock
, &Fds
);
600 Timeval
.tv_sec
= Timeout
/ 1000;
601 Timeval
.tv_usec
= Timeout
% 1000;
602 Status
= select(0, NULL
, &Fds
, NULL
, &Timeval
);
603 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
607 printf("Sending packet\n");
608 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
612 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
613 0, (SOCKADDR
*)&Target
, sizeof(Target
));
614 QueryTime(&SentTime
);
617 if (Status
== SOCKET_ERROR
)
619 if (WSAGetLastError() == WSAEHOSTUNREACH
)
620 FormatOutput(IDS_DEST_UNREACHABLE
);
622 FormatOutput(IDS_COULD_NOT_TRANSMIT
, WSAGetLastError());
627 /* Expect to receive ICMP echo reply */
629 FD_SET(IcmpSock
, &Fds
);
630 Timeval
.tv_sec
= Timeout
/ 1000;
631 Timeval
.tv_usec
= Timeout
% 1000;
634 Status
= select(0, &Fds
, NULL
, NULL
, &Timeval
);
635 if ((Status
!= SOCKET_ERROR
) && (Status
!= 0))
637 Length
= sizeof(From
);
638 Status
= recvfrom(IcmpSock
, Buffer
, Size
, 0, &From
, &Length
);
641 printf("Received packet\n");
642 DisplayBuffer(Buffer
, Status
);
648 if (Status
== SOCKET_ERROR
)
650 if (WSAGetLastError() != WSAETIMEDOUT
)
652 FormatOutput(IDS_COULD_NOT_RECV
, WSAGetLastError());
661 FormatOutput(IDS_REQUEST_TIMEOUT
);
666 } while (!DecodeResponse(Buffer
, Status
, (PSOCKADDR_IN
)&From
));
673 /* Program entry point */
674 int wmain(int argc
, LPWSTR argv
[])
681 hStdOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
685 if ((ParseCmdline(argc
, argv
)) && (Setup()))
688 FormatOutput(IDS_PING_WITH_BYTES
, TargetName
, TargetIP
, DataSize
);
691 while ((NeverStop
) || (Count
< PingCount
))
695 if((NeverStop
) || (Count
< PingCount
))
701 /* Calculate avarage round trip time */
702 if ((SentCount
- LostCount
) > 0)
703 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
707 /* Calculate loss percent */
708 Count
= SentCount
? (LostCount
* 100) / SentCount
: 0;
713 TimeToMsString(MinTime
, MinRTT
);
714 TimeToMsString(MaxTime
, MaxRTT
);
715 TimeToMsString(AvgTime
, AvgRTT
);
717 /* Print statistics */
718 FormatOutput(IDS_PING_STATISTICS
, TargetIP
);
719 FormatOutput(IDS_PACKETS_SENT_RECEIVED_LOST
,\
720 SentCount
, SentCount
- LostCount
, LostCount
, Count
);
723 /* Print approximate times or NO approximate times if 100% loss */
724 if ((SentCount
- LostCount
) > 0)
726 FormatOutput(IDS_APPROXIMATE_ROUND_TRIP
);
727 FormatOutput(IDS_MIN_MAX_AVERAGE
, MinTime
, MaxTime
, AvgTime
);