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
= iSeqNum
;
265 /* calculate checksum of packet */
266 pInfo
->SendPacket
->icmpheader
.checksum
= CheckSum((PUSHORT
)&pInfo
->SendPacket
,
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
, //buffer
283 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
);
380 TTL_EXCEED_HEADER
*TTLExceedHdr
= (TTL_EXCEED_HEADER
*)((char *)pInfo
->RecvPacket
+ header_len
);
382 /* Make sure the reply is ok */
383 if (PACKET_SIZE
< header_len
+ ICMP_MIN_SIZE
)
385 DebugPrint(_T("too few bytes from %s\n"), inet_ntoa(pInfo
->dest
.sin_addr
));
389 switch (IcmpHdr
->icmpheader
.type
)
392 if (TTLExceedHdr
->OrigIcmpHeader
.id
!= (USHORT
)GetCurrentProcessId())
394 /* FIXME: our network stack shouldn't allow this... */
395 /* we've picked up a packet not related to this process probably from another local program. We ignore it */
396 DebugPrint(_T("Rouge packet: header id, process id %d"), TTLExceedHdr
->OrigIcmpHeader
.id
, GetCurrentProcessId());
399 _tprintf(_T("%3Ld ms"), (pInfo
->lTimeEnd
- pInfo
->lTimeStart
) / pInfo
->TicksPerMs
.QuadPart
);
403 if (IcmpHdr
->icmpheader
.id
!= (USHORT
)GetCurrentProcessId())
405 /* FIXME: our network stack shouldn't allow this... */
406 /* we've picked up a packet not related to this process probably from another local program. We ignore it */
407 DebugPrint(_T("Rouge packet: header id %d, process id %d"), IcmpHdr
->icmpheader
.id
, GetCurrentProcessId());
410 _tprintf(_T("%3Ld ms"), (pInfo
->lTimeEnd
- pInfo
->lTimeStart
) / pInfo
->TicksPerMs
.QuadPart
);
413 case DEST_UNREACHABLE
:
423 AllocateBuffers(PAPPINFO pInfo
)
425 pInfo
->SendPacket
= (PECHO_REPLY_HEADER
)HeapAlloc(GetProcessHeap(),
427 sizeof(ECHO_REPLY_HEADER
) + PACKET_SIZE
);
428 if (!pInfo
->SendPacket
)
431 pInfo
->RecvPacket
= (PIPv4_HEADER
)HeapAlloc(GetProcessHeap(),
433 sizeof(IPv4_HEADER
) + PACKET_SIZE
);
434 if (!pInfo
->RecvPacket
)
436 HeapFree(GetProcessHeap(),
448 Driver(PAPPINFO pInfo
)
450 INT iHopCount
= 1; // hop counter. default max is 30
451 BOOL bFoundTarget
= FALSE
; // Have we reached our destination yet
452 INT iRecieveReturn
; // RecieveReturn return value
453 PECHO_REPLY_HEADER icmphdr
;
458 //temps for getting host name
463 SetupTimingMethod(pInfo
);
465 if (AllocateBuffers(pInfo
) &&
466 ResolveHostname(pInfo
) &&
469 /* print tracing info to screen */
470 _tprintf(_T("\nTracing route to %s [%s]\n"), cHostname
, cDestIP
);
471 _tprintf(_T("over a maximum of %d hop"), pInfo
->iMaxHops
);
472 pInfo
->iMaxHops
> 1 ? _tprintf(_T("s:\n\n")) : _tprintf(_T(":\n\n"));
474 /* run until we hit either max hops, or find the target */
475 while ((iHopCount
<= pInfo
->iMaxHops
) &&
476 (bFoundTarget
!= TRUE
))
481 _tprintf(_T("%3d "), iHopCount
);
483 /* run 3 pings for each hop */
484 for (i
= 0; i
< 3; i
++)
486 if (SetTTL(pInfo
->icmpSock
, iTTL
) != TRUE
)
488 DebugPrint(_T("error in Setup()\n"));
492 PreparePacket(pInfo
, iSeqNum
);
494 if (SendPacket(pInfo
) != SOCKET_ERROR
)
496 BOOL bAwaitPacket
= FALSE
; // indicates whether we have recieved a good packet
500 /* Receive replies until we get a successful read, or a fatal error */
501 if ((iRecieveReturn
= ReceivePacket(pInfo
)) < 0)
503 /* FIXME: consider moving this into RecievePacket */
504 /* check the seq num in the packet, if it's bad wait for another */
505 WORD hdrLen
= pInfo
->RecvPacket
->h_len
* 4;
506 icmphdr
= (ECHO_REPLY_HEADER
*)((char*)&pInfo
->RecvPacket
+ hdrLen
);
507 if (icmphdr
->icmpheader
.seq
!= iSeqNum
)
509 _tprintf(_T("bad sequence number!\n"));
516 DecodeResponse(pInfo
);
519 /* packet timed out. Don't wait for it again */
520 bAwaitPacket
= FALSE
;
522 } while (bAwaitPacket
);
529 if(pInfo
->bResolveAddresses
)
531 INT iNameInfoRet
; // getnameinfo return value
532 /* gethostbyaddr() and getnameinfo() are
533 * unimplemented in ROS at present.
534 * Alex has advised he will be implementing getnameinfo.
535 * I've used that for the time being for testing in Windows*/
537 //ip = inet_addr(inet_ntoa(source.sin_addr));
538 //host = gethostbyaddr((char *)&ip, 4, 0);
540 ip
= inet_ntoa(pInfo
->source
.sin_addr
);
542 iNameInfoRet
= getnameinfo((SOCKADDR
*)&pInfo
->source
,
549 if (iNameInfoRet
== 0)
551 /* if IP address resolved to a hostname,
552 * print the IP address after it */
553 if (lstrcmpA(cHost
, ip
) != 0)
554 _tprintf(_T("%s [%s]"), cHost
, ip
);
556 _tprintf(_T("%s"), cHost
);
560 DebugPrint(_T("error: %d"), WSAGetLastError());
561 DebugPrint(_T(" getnameinfo failed: %d"), iNameInfoRet
);
566 _tprintf(_T("%s"), inet_ntoa(pInfo
->source
.sin_addr
));
570 /* check if we've arrived at the target */
571 if (strcmp(cDestIP
, inet_ntoa(pInfo
->source
.sin_addr
)) == 0)
580 _tprintf(_T("\nTrace complete.\n"));
589 Cleanup(PAPPINFO pInfo
)
592 closesocket(pInfo
->icmpSock
);
596 if (pInfo
->SendPacket
)
597 HeapFree(GetProcessHeap(),
601 if (pInfo
->SendPacket
)
602 HeapFree(GetProcessHeap(),
608 #if defined(_UNICODE) && defined(__GNUC__)
611 int _tmain(int argc
, LPCTSTR argv
[])
617 pInfo
= (PAPPINFO
)HeapAlloc(GetProcessHeap(),
622 pInfo
->bResolveAddresses
= TRUE
;
623 pInfo
->iMaxHops
= 30;
624 pInfo
->iTimeOut
= 1000;
626 if (ParseCmdline(argc
, argv
, pInfo
))
628 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
630 DebugPrint(_T("WSAStartup failed.\n"));
639 HeapFree(GetProcessHeap(),
648 #if defined(_UNICODE) && defined(__GNUC__)
649 /* HACK - MINGW HAS NO OFFICIAL SUPPORT FOR wmain()!!! */
650 int main( int argc
, char **argv
)
655 if ((argvW
= malloc(argc
* sizeof(WCHAR
*))))
657 /* convert the arguments */
658 for (i
= 0, j
= 0; i
< argc
; i
++)
660 if (!(argvW
[i
] = malloc((strlen(argv
[i
]) + 1) * sizeof(WCHAR
))))
664 swprintf(argvW
[i
], L
"%hs", argv
[i
]);
669 /* no error converting the parameters, call wmain() */
670 Ret
= wmain(argc
, (LPCTSTR
*)argvW
);
673 /* free the arguments */
674 for (i
= 0; i
< argc
; i
++)