2 * PROJECT: ReactOS trace route utility
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/network/tracert.c
5 * PURPOSE: Trace network paths through networks
6 * COPYRIGHT: Copyright 2006 - 2007 Ged Murphy <gedmurphy@reactos.org>
14 CHAR cHostname
[256]; // target hostname
15 CHAR cDestIP
[18]; // target IP
19 DebugPrint(LPTSTR lpString
, ...)
23 va_start(args
, lpString
);
24 _vtprintf(lpString
, args
);
27 UNREFERENCED_PARAMETER(lpString
);
35 _tprintf(_T("\nUsage: tracert [-d] [-h maximum_hops] [-j host-list] [-w timeout] target_name\n\n"
37 " -d Do not resolve addresses to hostnames.\n"
38 " -h maximum_hops Maximum number of hops to search for target.\n"
39 " -j host-list Loose source route along host-list.\n"
40 " -w timeout Wait timeout milliseconds for each reply.\n\n"));
42 _tprintf(_T("NOTES\n-----\n"
43 "- Setting TTL values is not currently supported in ReactOS, so the trace will\n"
44 " jump straight to the destination. This feature will be implemented soon.\n"
45 "- Host info is not currently available in ReactOS and will fail with strange\n"
46 " results. Use -d to force it not to resolve IP's.\n"
47 "- For testing purposes, all should work as normal in a Windows environment\n\n"));
52 ParseCmdline(int argc
,
65 for (i
= 1; i
< argc
; i
++)
67 if (argv
[i
][0] == _T('-'))
72 pInfo
->bResolveAddresses
= FALSE
;
76 _stscanf(argv
[i
+1], _T("%d"), &pInfo
->iMaxHops
);
80 _tprintf(_T("-j is not yet implemented.\n"));
84 _stscanf(argv
[i
+1], _T("%d"), &pInfo
->iTimeOut
);
89 _tprintf(_T("%s is not a valid option.\n"), argv
[i
]);
96 /* copy target address */
97 _tcsncpy(cHostname
, argv
[i
], 255);
106 CheckSum(PUSHORT data
,
114 size
-= sizeof(USHORT
);
118 dwSum
+= *(UCHAR
*)data
;
120 dwSum
= (dwSum
>> 16) + (dwSum
& 0xFFFF);
121 dwSum
+= (dwSum
>> 16);
123 return (USHORT
)(~dwSum
);
128 SetupTimingMethod(PAPPINFO pInfo
)
130 LARGE_INTEGER PerformanceCounterFrequency
;
132 /* check if performance counters are available */
133 pInfo
->bUsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
135 if (pInfo
->bUsePerformanceCounter
)
137 /* restrict execution to first processor on SMP systems */
138 if (SetThreadAffinityMask(GetCurrentThread(), 1) == 0)
139 pInfo
->bUsePerformanceCounter
= FALSE
;
141 pInfo
->TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
142 pInfo
->TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
146 pInfo
->TicksPerMs
.QuadPart
= 1;
147 pInfo
->TicksPerUs
.QuadPart
= 1;
153 ResolveHostname(PAPPINFO pInfo
)
158 ZeroMemory(&pInfo
->dest
, sizeof(pInfo
->dest
));
160 /* if address is not a dotted decimal */
161 if ((addr
= inet_addr(cHostname
))== INADDR_NONE
)
163 if ((hp
= gethostbyname(cHostname
)) != 0)
165 //CopyMemory(&pInfo->dest.sin_addr, hp->h_addr, hp->h_length);
166 pInfo
->dest
.sin_addr
= *((struct in_addr
*)hp
->h_addr
);
167 pInfo
->dest
.sin_family
= hp
->h_addrtype
;
171 _tprintf(_T("Unable to resolve target system name %s.\n"), cHostname
);
177 pInfo
->dest
.sin_addr
.s_addr
= addr
;
178 pInfo
->dest
.sin_family
= AF_INET
;
181 _tcscpy(cDestIP
, inet_ntoa(pInfo
->dest
.sin_addr
));
188 GetTime(PAPPINFO pInfo
)
192 /* Get the system time using preformance counters if available */
193 if (pInfo
->bUsePerformanceCounter
)
195 if (QueryPerformanceCounter(&Time
))
197 return Time
.QuadPart
;
201 /* otherwise fall back to GetTickCount */
202 Time
.u
.LowPart
= (DWORD
)GetTickCount();
205 return (LONGLONG
)Time
.u
.LowPart
;
217 sizeof(iTTL
)) == SOCKET_ERROR
)
219 DebugPrint(_T("TTL setsockopt failed : %d. \n"), WSAGetLastError());
228 CreateSocket(PAPPINFO pInfo
)
230 pInfo
->icmpSock
= WSASocket(AF_INET
,
237 if (pInfo
->icmpSock
== INVALID_SOCKET
)
239 INT err
= WSAGetLastError();
240 DebugPrint(_T("Could not create socket : %d.\n"), err
);
242 if (err
== WSAEACCES
)
244 _tprintf(_T("\n\nYou must have access to raw sockets (admin) to run this program!\n\n"));
255 PreparePacket(PAPPINFO pInfo
,
258 /* assemble ICMP echo request packet */
259 pInfo
->SendPacket
->icmpheader
.type
= ECHO_REQUEST
;
260 pInfo
->SendPacket
->icmpheader
.code
= 0;
261 pInfo
->SendPacket
->icmpheader
.checksum
= 0;
262 pInfo
->SendPacket
->icmpheader
.id
= (USHORT
)GetCurrentProcessId();
263 pInfo
->SendPacket
->icmpheader
.seq
= htons((USHORT
)iSeqNum
);
265 /* calculate checksum of packet */
266 pInfo
->SendPacket
->icmpheader
.checksum
= CheckSum((PUSHORT
)&pInfo
->SendPacket
->icmpheader
,
267 sizeof(ICMP_HEADER
) + PACKET_SIZE
);
272 SendPacket(PAPPINFO pInfo
)
276 DebugPrint(_T("\nsending packet of %d bytes... "), PACKET_SIZE
);
278 /* get time packet was sent */
279 pInfo
->lTimeStart
= GetTime(pInfo
);
281 iSockRet
= sendto(pInfo
->icmpSock
, //socket
282 (char *)&pInfo
->SendPacket
->icmpheader
,//buffer
283 sizeof(ICMP_HEADER
) + PACKET_SIZE
,//size of buffer
285 (SOCKADDR
*)&pInfo
->dest
, //destination
286 sizeof(pInfo
->dest
)); //address length
288 if (iSockRet
== SOCKET_ERROR
)
290 if (WSAGetLastError() == WSAEACCES
)
292 /* FIXME: Is this correct? */
293 _tprintf(_T("\n\nYou must be an administrator to run this program!\n\n"));
295 HeapFree(GetProcessHeap(), 0, pInfo
);
300 DebugPrint(_T("sendto failed %d\n"), WSAGetLastError());
305 DebugPrint(_T("sent %d bytes\n"), iSockRet
);
313 ReceivePacket(PAPPINFO pInfo
)
317 INT iSockRet
= 0, iSelRet
;
321 iFromLen
= sizeof(pInfo
->source
);
323 DebugPrint(_T("Receiving packet. Available buffer, %d bytes... "), MAX_PING_PACKET_SIZE
);
325 /* monitor icmpSock for incomming connections */
327 FD_SET(pInfo
->icmpSock
, &readFDS
);
329 /* set timeout values */
330 timeVal
.tv_sec
= pInfo
->iTimeOut
/ 1000;
331 timeVal
.tv_usec
= pInfo
->iTimeOut
% 1000;
339 if (iSelRet
== SOCKET_ERROR
)
341 DebugPrint(_T("select() failed in sendPacket() %d\n"), WSAGetLastError());
343 else if (iSelRet
== 0) /* if socket timed out */
347 else if ((iSelRet
!= SOCKET_ERROR
) && (iSelRet
!= 0))
349 iSockRet
= recvfrom(pInfo
->icmpSock
, // socket
350 (char *)pInfo
->RecvPacket
, // buffer
351 MAX_PING_PACKET_SIZE
, // size of buffer
353 (SOCKADDR
*)&pInfo
->source
, // source address
354 &iFromLen
); // address length
356 if (iSockRet
!= SOCKET_ERROR
)
358 /* get time packet was recieved */
359 pInfo
->lTimeEnd
= GetTime(pInfo
);
360 DebugPrint(_T("reveived %d bytes\n"), iSockRet
);
365 DebugPrint(_T("recvfrom failed: %d\n"), WSAGetLastError());
374 DecodeResponse(PAPPINFO pInfo
)
376 unsigned short header_len
= pInfo
->RecvPacket
->h_len
* 4;
378 /* cast the recieved packet into an ECHO reply and a TTL Exceed and check the ID*/
379 ECHO_REPLY_HEADER
*IcmpHdr
= (ECHO_REPLY_HEADER
*)((char*)pInfo
->RecvPacket
+ header_len
);
381 /* Make sure the reply is ok */
382 if (PACKET_SIZE
< header_len
+ ICMP_MIN_SIZE
)
384 DebugPrint(_T("too few bytes from %s\n"), inet_ntoa(pInfo
->dest
.sin_addr
));
388 switch (IcmpHdr
->icmpheader
.type
)
391 _tprintf(_T("%3ld ms"), (ULONG
)((pInfo
->lTimeEnd
- pInfo
->lTimeStart
) / pInfo
->TicksPerMs
.QuadPart
));
395 if (IcmpHdr
->icmpheader
.id
!= (USHORT
)GetCurrentProcessId())
397 /* FIXME: our network stack shouldn't allow this... */
398 /* we've picked up a packet not related to this process probably from another local program. We ignore it */
399 DebugPrint(_T("Rouge packet: header id %d, process id %d"), IcmpHdr
->icmpheader
.id
, GetCurrentProcessId());
402 _tprintf(_T("%3ld ms"), (ULONG
)((pInfo
->lTimeEnd
- pInfo
->lTimeStart
) / pInfo
->TicksPerMs
.QuadPart
));
405 case DEST_UNREACHABLE
:
415 AllocateBuffers(PAPPINFO pInfo
)
417 pInfo
->SendPacket
= (PECHO_REPLY_HEADER
)HeapAlloc(GetProcessHeap(),
419 sizeof(ECHO_REPLY_HEADER
) + PACKET_SIZE
);
420 if (!pInfo
->SendPacket
)
423 pInfo
->RecvPacket
= (PIPv4_HEADER
)HeapAlloc(GetProcessHeap(),
425 sizeof(IPv4_HEADER
) + PACKET_SIZE
);
426 if (!pInfo
->RecvPacket
)
428 HeapFree(GetProcessHeap(),
440 Driver(PAPPINFO pInfo
)
442 INT iHopCount
= 1; // hop counter. default max is 30
443 BOOL bFoundTarget
= FALSE
; // Have we reached our destination yet
444 INT iRecieveReturn
; // RecieveReturn return value
445 PECHO_REPLY_HEADER icmphdr
;
450 //temps for getting host name
455 SetupTimingMethod(pInfo
);
457 if (AllocateBuffers(pInfo
) &&
458 ResolveHostname(pInfo
) &&
461 /* print tracing info to screen */
462 _tprintf(_T("\nTracing route to %s [%s]\n"), cHostname
, cDestIP
);
463 _tprintf(_T("over a maximum of %d hop"), pInfo
->iMaxHops
);
464 pInfo
->iMaxHops
> 1 ? _tprintf(_T("s:\n\n")) : _tprintf(_T(":\n\n"));
466 /* run until we hit either max hops, or find the target */
467 while ((iHopCount
<= pInfo
->iMaxHops
) &&
468 (bFoundTarget
!= TRUE
))
473 _tprintf(_T("%3d "), iHopCount
);
475 /* run 3 pings for each hop */
476 for (i
= 0; i
< 3; i
++)
478 if (SetTTL(pInfo
->icmpSock
, iTTL
) != TRUE
)
480 DebugPrint(_T("error in Setup()\n"));
484 PreparePacket(pInfo
, iSeqNum
);
486 if (SendPacket(pInfo
) != SOCKET_ERROR
)
488 BOOL bAwaitPacket
= FALSE
; // indicates whether we have recieved a good packet
492 /* Receive replies until we get a successful read, or a fatal error */
493 if ((iRecieveReturn
= ReceivePacket(pInfo
)) < 0)
495 /* FIXME: consider moving this into RecievePacket */
496 /* check the seq num in the packet, if it's bad wait for another */
497 WORD hdrLen
= pInfo
->RecvPacket
->h_len
* 4;
498 icmphdr
= (ECHO_REPLY_HEADER
*)((char*)&pInfo
->RecvPacket
+ hdrLen
);
499 if (icmphdr
->icmpheader
.seq
!= iSeqNum
)
501 _tprintf(_T("bad sequence number!\n"));
508 if (DecodeResponse(pInfo
) < 0)
512 } while (bAwaitPacket
);
519 if(pInfo
->bResolveAddresses
)
521 INT iNameInfoRet
; // getnameinfo return value
522 /* gethostbyaddr() and getnameinfo() are
523 * unimplemented in ROS at present.
524 * Alex has advised he will be implementing getnameinfo.
525 * I've used that for the time being for testing in Windows*/
527 //ip = inet_addr(inet_ntoa(source.sin_addr));
528 //host = gethostbyaddr((char *)&ip, 4, 0);
530 ip
= inet_ntoa(pInfo
->source
.sin_addr
);
532 iNameInfoRet
= getnameinfo((SOCKADDR
*)&pInfo
->source
,
539 if (iNameInfoRet
== 0)
541 /* if IP address resolved to a hostname,
542 * print the IP address after it */
543 if (lstrcmpA(cHost
, ip
) != 0)
544 _tprintf(_T("%s [%s]"), cHost
, ip
);
546 _tprintf(_T("%s"), cHost
);
550 DebugPrint(_T("error: %d"), WSAGetLastError());
551 DebugPrint(_T(" getnameinfo failed: %d"), iNameInfoRet
);
552 _tprintf(_T("%s"), inet_ntoa(pInfo
->source
.sin_addr
));
557 _tprintf(_T("%s"), inet_ntoa(pInfo
->source
.sin_addr
));
561 /* check if we've arrived at the target */
562 if (strcmp(cDestIP
, inet_ntoa(pInfo
->source
.sin_addr
)) == 0)
571 _tprintf(_T("\nTrace complete.\n"));
580 Cleanup(PAPPINFO pInfo
)
583 closesocket(pInfo
->icmpSock
);
587 if (pInfo
->SendPacket
)
588 HeapFree(GetProcessHeap(),
592 if (pInfo
->RecvPacket
)
593 HeapFree(GetProcessHeap(),
599 #if defined(_UNICODE) && defined(__GNUC__)
602 int _tmain(int argc
, LPCTSTR argv
[])
608 pInfo
= (PAPPINFO
)HeapAlloc(GetProcessHeap(),
613 pInfo
->bResolveAddresses
= TRUE
;
614 pInfo
->iMaxHops
= 30;
615 pInfo
->iTimeOut
= 1000;
617 if (ParseCmdline(argc
, argv
, pInfo
))
619 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
621 DebugPrint(_T("WSAStartup failed.\n"));
630 HeapFree(GetProcessHeap(),
639 #if defined(_UNICODE) && defined(__GNUC__)
640 /* HACK - MINGW HAS NO OFFICIAL SUPPORT FOR wmain()!!! */
641 int main( int argc
, char **argv
)
646 if ((argvW
= malloc(argc
* sizeof(WCHAR
*))))
648 /* convert the arguments */
649 for (i
= 0, j
= 0; i
< argc
; i
++)
651 if (!(argvW
[i
] = malloc((strlen(argv
[i
]) + 1) * sizeof(WCHAR
))))
655 swprintf(argvW
[i
], L
"%hs", argv
[i
]);
660 /* no error converting the parameters, call wmain() */
661 Ret
= wmain(argc
, (LPCTSTR
*)argvW
);
664 /* free the arguments */
665 for (i
= 0; i
< argc
; i
++)