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
18 DebugPrint(LPTSTR lpString
, ...)
22 va_start(args
, lpString
);
23 _vtprintf(lpString
, args
);
26 UNREFERENCED_PARAMETER(lpString
);
33 _tprintf(_T("\nUsage: tracert [-d] [-h maximum_hops] [-j host-list] [-w timeout] target_name\n\n"
35 " -d Do not resolve addresses to hostnames.\n"
36 " -h maximum_hops Maximum number of hops to search for target.\n"
37 " -j host-list Loose source route along host-list.\n"
38 " -w timeout Wait timeout milliseconds for each reply.\n\n"));
40 _tprintf(_T("NOTES\n-----\n"
41 "- Setting TTL values is not currently supported in ReactOS, so the trace will\n"
42 " jump straight to the destination. This feature will be implemented soon.\n"
43 "- Host info is not currently available in ReactOS and will fail with strange\n"
44 " results. Use -d to force it not to resolve IP's.\n"
45 "- For testing purposes, all should work as normal in a Windows environment\n\n"));
49 ParseCmdline(int argc
,
62 for (i
= 1; i
< argc
; i
++)
64 if (argv
[i
][0] == _T('-'))
69 pInfo
->bResolveAddresses
= FALSE
;
73 _stscanf(argv
[i
+1], _T("%d"), &pInfo
->iMaxHops
);
77 _tprintf(_T("-j is not yet implemented.\n"));
81 _stscanf(argv
[i
+1], _T("%d"), &pInfo
->iTimeOut
);
86 _tprintf(_T("%s is not a valid option.\n"), argv
[i
]);
93 /* copy target address */
94 _tcsncpy(cHostname
, argv
[i
], 255);
102 CheckSum(PUSHORT data
,
110 size
-= sizeof(USHORT
);
114 dwSum
+= *(UCHAR
*)data
;
116 dwSum
= (dwSum
>> 16) + (dwSum
& 0xFFFF);
117 dwSum
+= (dwSum
>> 16);
119 return (USHORT
)(~dwSum
);
123 SetupTimingMethod(PAPPINFO pInfo
)
125 LARGE_INTEGER PerformanceCounterFrequency
;
127 /* check if performance counters are available */
128 pInfo
->bUsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
130 if (pInfo
->bUsePerformanceCounter
)
132 /* restrict execution to first processor on SMP systems */
133 if (SetThreadAffinityMask(GetCurrentThread(), 1) == 0)
134 pInfo
->bUsePerformanceCounter
= FALSE
;
136 pInfo
->TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
137 pInfo
->TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
141 pInfo
->TicksPerMs
.QuadPart
= 1;
142 pInfo
->TicksPerUs
.QuadPart
= 1;
147 ResolveHostname(PAPPINFO pInfo
)
152 ZeroMemory(&pInfo
->dest
, sizeof(pInfo
->dest
));
154 /* if address is not a dotted decimal */
155 if ((addr
= inet_addr(cHostname
))== INADDR_NONE
)
157 if ((hp
= gethostbyname(cHostname
)) != 0)
159 //CopyMemory(&pInfo->dest.sin_addr, hp->h_addr, hp->h_length);
160 pInfo
->dest
.sin_addr
= *((struct in_addr
*)hp
->h_addr
);
161 pInfo
->dest
.sin_family
= hp
->h_addrtype
;
165 _tprintf(_T("Unable to resolve target system name %s.\n"), cHostname
);
171 pInfo
->dest
.sin_addr
.s_addr
= addr
;
172 pInfo
->dest
.sin_family
= AF_INET
;
175 _tcscpy(cDestIP
, inet_ntoa(pInfo
->dest
.sin_addr
));
181 GetTime(PAPPINFO pInfo
)
185 /* Get the system time using preformance counters if available */
186 if (pInfo
->bUsePerformanceCounter
)
188 if (QueryPerformanceCounter(&Time
))
190 return Time
.QuadPart
;
194 /* otherwise fall back to GetTickCount */
195 Time
.u
.LowPart
= (DWORD
)GetTickCount();
198 return (LONGLONG
)Time
.u
.LowPart
;
209 sizeof(iTTL
)) == SOCKET_ERROR
)
211 DebugPrint(_T("TTL setsockopt failed : %d. \n"), WSAGetLastError());
219 CreateSocket(PAPPINFO pInfo
)
221 pInfo
->icmpSock
= WSASocket(AF_INET
,
228 if (pInfo
->icmpSock
== INVALID_SOCKET
)
230 INT err
= WSAGetLastError();
231 DebugPrint(_T("Could not create socket : %d.\n"), err
);
233 if (err
== WSAEACCES
)
235 _tprintf(_T("\n\nYou must have access to raw sockets (admin) to run this program!\n\n"));
245 PreparePacket(PAPPINFO pInfo
,
248 /* assemble ICMP echo request packet */
249 pInfo
->SendPacket
->icmpheader
.type
= ECHO_REQUEST
;
250 pInfo
->SendPacket
->icmpheader
.code
= 0;
251 pInfo
->SendPacket
->icmpheader
.checksum
= 0;
252 pInfo
->SendPacket
->icmpheader
.id
= (USHORT
)GetCurrentProcessId();
253 pInfo
->SendPacket
->icmpheader
.seq
= htons((USHORT
)iSeqNum
);
255 /* calculate checksum of packet */
256 pInfo
->SendPacket
->icmpheader
.checksum
= CheckSum((PUSHORT
)&pInfo
->SendPacket
->icmpheader
,
257 sizeof(ICMP_HEADER
) + PACKET_SIZE
);
261 SendPacket(PAPPINFO pInfo
)
265 DebugPrint(_T("\nsending packet of %d bytes... "), PACKET_SIZE
);
267 /* get time packet was sent */
268 pInfo
->lTimeStart
= GetTime(pInfo
);
270 iSockRet
= sendto(pInfo
->icmpSock
, //socket
271 (char *)&pInfo
->SendPacket
->icmpheader
,//buffer
272 sizeof(ICMP_HEADER
) + PACKET_SIZE
,//size of buffer
274 (SOCKADDR
*)&pInfo
->dest
, //destination
275 sizeof(pInfo
->dest
)); //address length
277 if (iSockRet
== SOCKET_ERROR
)
279 if (WSAGetLastError() == WSAEACCES
)
281 /* FIXME: Is this correct? */
282 _tprintf(_T("\n\nYou must be an administrator to run this program!\n\n"));
284 HeapFree(GetProcessHeap(), 0, pInfo
);
289 DebugPrint(_T("sendto failed %d\n"), WSAGetLastError());
294 DebugPrint(_T("sent %d bytes\n"), iSockRet
);
301 ReceivePacket(PAPPINFO pInfo
)
305 INT iSockRet
= 0, iSelRet
;
309 iFromLen
= sizeof(pInfo
->source
);
311 DebugPrint(_T("Receiving packet. Available buffer, %d bytes... "), MAX_PING_PACKET_SIZE
);
313 /* monitor icmpSock for incomming connections */
315 FD_SET(pInfo
->icmpSock
, &readFDS
);
317 /* set timeout values */
318 timeVal
.tv_sec
= pInfo
->iTimeOut
/ 1000;
319 timeVal
.tv_usec
= pInfo
->iTimeOut
% 1000;
327 if (iSelRet
== SOCKET_ERROR
)
329 DebugPrint(_T("select() failed in sendPacket() %d\n"), WSAGetLastError());
331 else if (iSelRet
== 0) /* if socket timed out */
335 else if ((iSelRet
!= SOCKET_ERROR
) && (iSelRet
!= 0))
337 iSockRet
= recvfrom(pInfo
->icmpSock
, // socket
338 (char *)pInfo
->RecvPacket
, // buffer
339 MAX_PING_PACKET_SIZE
, // size of buffer
341 (SOCKADDR
*)&pInfo
->source
, // source address
342 &iFromLen
); // address length
344 if (iSockRet
!= SOCKET_ERROR
)
346 /* get time packet was recieved */
347 pInfo
->lTimeEnd
= GetTime(pInfo
);
348 DebugPrint(_T("reveived %d bytes\n"), iSockRet
);
353 DebugPrint(_T("recvfrom failed: %d\n"), WSAGetLastError());
361 DecodeResponse(PAPPINFO pInfo
)
363 unsigned short header_len
= pInfo
->RecvPacket
->h_len
* 4;
365 /* cast the recieved packet into an ECHO reply and a TTL Exceed and check the ID*/
366 ECHO_REPLY_HEADER
*IcmpHdr
= (ECHO_REPLY_HEADER
*)((char*)pInfo
->RecvPacket
+ header_len
);
368 /* Make sure the reply is ok */
369 if (PACKET_SIZE
< header_len
+ ICMP_MIN_SIZE
)
371 DebugPrint(_T("too few bytes from %s\n"), inet_ntoa(pInfo
->dest
.sin_addr
));
375 switch (IcmpHdr
->icmpheader
.type
)
378 _tprintf(_T("%3ld ms"), (ULONG
)((pInfo
->lTimeEnd
- pInfo
->lTimeStart
) / pInfo
->TicksPerMs
.QuadPart
));
382 if (IcmpHdr
->icmpheader
.id
!= (USHORT
)GetCurrentProcessId())
384 /* FIXME: our network stack shouldn't allow this... */
385 /* we've picked up a packet not related to this process probably from another local program. We ignore it */
386 DebugPrint(_T("Rouge packet: header id %d, process id %d"), IcmpHdr
->icmpheader
.id
, GetCurrentProcessId());
389 _tprintf(_T("%3ld ms"), (ULONG
)((pInfo
->lTimeEnd
- pInfo
->lTimeStart
) / pInfo
->TicksPerMs
.QuadPart
));
392 case DEST_UNREACHABLE
:
401 AllocateBuffers(PAPPINFO pInfo
)
403 pInfo
->SendPacket
= (PECHO_REPLY_HEADER
)HeapAlloc(GetProcessHeap(),
405 sizeof(ECHO_REPLY_HEADER
) + PACKET_SIZE
);
406 if (!pInfo
->SendPacket
)
409 pInfo
->RecvPacket
= (PIPv4_HEADER
)HeapAlloc(GetProcessHeap(),
411 MAX_PING_PACKET_SIZE
);
412 if (!pInfo
->RecvPacket
)
414 HeapFree(GetProcessHeap(),
425 Driver(PAPPINFO pInfo
)
427 INT iHopCount
= 1; // hop counter. default max is 30
428 BOOL bFoundTarget
= FALSE
; // Have we reached our destination yet
429 INT iRecieveReturn
; // RecieveReturn return value
430 PECHO_REPLY_HEADER icmphdr
;
435 //temps for getting host name
440 SetupTimingMethod(pInfo
);
442 if (AllocateBuffers(pInfo
) &&
443 ResolveHostname(pInfo
) &&
446 /* print tracing info to screen */
447 _tprintf(_T("\nTracing route to %s [%s]\n"), cHostname
, cDestIP
);
448 _tprintf(_T("over a maximum of %d hop"), pInfo
->iMaxHops
);
449 pInfo
->iMaxHops
> 1 ? _tprintf(_T("s:\n\n")) : _tprintf(_T(":\n\n"));
451 /* run until we hit either max hops, or find the target */
452 while ((iHopCount
<= pInfo
->iMaxHops
) &&
453 (bFoundTarget
!= TRUE
))
458 _tprintf(_T("%3d "), iHopCount
);
460 /* run 3 pings for each hop */
461 for (i
= 0; i
< 3; i
++)
463 if (SetTTL(pInfo
->icmpSock
, iTTL
) != TRUE
)
465 DebugPrint(_T("error in Setup()\n"));
469 PreparePacket(pInfo
, iSeqNum
);
471 if (SendPacket(pInfo
) != SOCKET_ERROR
)
473 BOOL bAwaitPacket
= FALSE
; // indicates whether we have recieved a good packet
477 /* Receive replies until we get a successful read, or a fatal error */
478 if ((iRecieveReturn
= ReceivePacket(pInfo
)) < 0)
480 /* FIXME: consider moving this into RecievePacket */
481 /* check the seq num in the packet, if it's bad wait for another */
482 WORD hdrLen
= pInfo
->RecvPacket
->h_len
* 4;
483 icmphdr
= (ECHO_REPLY_HEADER
*)((char*)&pInfo
->RecvPacket
+ hdrLen
);
484 if (icmphdr
->icmpheader
.seq
!= iSeqNum
)
486 _tprintf(_T("bad sequence number!\n"));
493 if (DecodeResponse(pInfo
) < 0)
497 } while (bAwaitPacket
);
504 if(pInfo
->bResolveAddresses
)
506 INT iNameInfoRet
; // getnameinfo return value
507 /* gethostbyaddr() and getnameinfo() are
508 * unimplemented in ROS at present.
509 * Alex has advised he will be implementing getnameinfo.
510 * I've used that for the time being for testing in Windows*/
512 //ip = inet_addr(inet_ntoa(source.sin_addr));
513 //host = gethostbyaddr((char *)&ip, 4, 0);
515 ip
= inet_ntoa(pInfo
->source
.sin_addr
);
517 iNameInfoRet
= getnameinfo((SOCKADDR
*)&pInfo
->source
,
524 if (iNameInfoRet
== 0)
526 /* if IP address resolved to a hostname,
527 * print the IP address after it */
528 if (lstrcmpA(cHost
, ip
) != 0)
529 _tprintf(_T("%s [%s]"), cHost
, ip
);
531 _tprintf(_T("%s"), cHost
);
535 DebugPrint(_T("error: %d"), WSAGetLastError());
536 DebugPrint(_T(" getnameinfo failed: %d"), iNameInfoRet
);
537 _tprintf(_T("%s"), inet_ntoa(pInfo
->source
.sin_addr
));
542 _tprintf(_T("%s"), inet_ntoa(pInfo
->source
.sin_addr
));
546 /* check if we've arrived at the target */
547 if (strcmp(cDestIP
, inet_ntoa(pInfo
->source
.sin_addr
)) == 0)
556 _tprintf(_T("\nTrace complete.\n"));
564 Cleanup(PAPPINFO pInfo
)
567 closesocket(pInfo
->icmpSock
);
571 if (pInfo
->SendPacket
)
572 HeapFree(GetProcessHeap(),
576 if (pInfo
->RecvPacket
)
577 HeapFree(GetProcessHeap(),
582 #if defined(_UNICODE) && defined(__GNUC__)
585 int _tmain(int argc
, LPCTSTR argv
[])
591 pInfo
= (PAPPINFO
)HeapAlloc(GetProcessHeap(),
596 pInfo
->bResolveAddresses
= TRUE
;
597 pInfo
->iMaxHops
= 30;
598 pInfo
->iTimeOut
= 1000;
600 if (ParseCmdline(argc
, argv
, pInfo
))
602 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0)
604 DebugPrint(_T("WSAStartup failed.\n"));
613 HeapFree(GetProcessHeap(),
621 #if defined(_UNICODE) && defined(__GNUC__)
622 /* HACK - MINGW HAS NO OFFICIAL SUPPORT FOR wmain()!!! */
623 int main( int argc
, char **argv
)
628 if ((argvW
= malloc(argc
* sizeof(WCHAR
*))))
630 /* convert the arguments */
631 for (i
= 0, j
= 0; i
< argc
; i
++)
633 if (!(argvW
[i
] = malloc((strlen(argv
[i
]) + 1) * sizeof(WCHAR
))))
637 swprintf(argvW
[i
], L
"%hs", argv
[i
]);
642 /* no error converting the parameters, call wmain() */
643 Ret
= wmain(argc
, (LPCTSTR
*)argvW
);
646 /* free the arguments */
647 for (i
= 0; i
< argc
; i
++)