Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentracert.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS trace route utility 00003 * LICENSE: GPL - See COPYING in the top level directory 00004 * FILE: base/applications/network/tracert.c 00005 * PURPOSE: Trace network paths through networks 00006 * COPYRIGHT: Copyright 2006 - 2007 Ged Murphy <gedmurphy@reactos.org> 00007 * 00008 */ 00009 00010 #include "tracert.h" 00011 00012 //#define TRACERT_DBG 00013 00014 CHAR cHostname[256]; // target hostname 00015 CHAR cDestIP[18]; // target IP 00016 00017 00018 static VOID 00019 DebugPrint(LPTSTR lpString, ...) 00020 { 00021 #ifdef TRACERT_DBG 00022 va_list args; 00023 va_start(args, lpString); 00024 _vtprintf(lpString, args); 00025 va_end(args); 00026 #else 00027 UNREFERENCED_PARAMETER(lpString); 00028 #endif 00029 } 00030 00031 00032 static VOID 00033 Usage(VOID) 00034 { 00035 _tprintf(_T("\nUsage: tracert [-d] [-h maximum_hops] [-j host-list] [-w timeout] target_name\n\n" 00036 "Options:\n" 00037 " -d Do not resolve addresses to hostnames.\n" 00038 " -h maximum_hops Maximum number of hops to search for target.\n" 00039 " -j host-list Loose source route along host-list.\n" 00040 " -w timeout Wait timeout milliseconds for each reply.\n\n")); 00041 00042 _tprintf(_T("NOTES\n-----\n" 00043 "- Setting TTL values is not currently supported in ReactOS, so the trace will\n" 00044 " jump straight to the destination. This feature will be implemented soon.\n" 00045 "- Host info is not currently available in ReactOS and will fail with strange\n" 00046 " results. Use -d to force it not to resolve IP's.\n" 00047 "- For testing purposes, all should work as normal in a Windows environment\n\n")); 00048 } 00049 00050 00051 static BOOL 00052 ParseCmdline(int argc, 00053 LPCTSTR argv[], 00054 PAPPINFO pInfo) 00055 { 00056 INT i; 00057 00058 if (argc < 2) 00059 { 00060 Usage(); 00061 return FALSE; 00062 } 00063 else 00064 { 00065 for (i = 1; i < argc; i++) 00066 { 00067 if (argv[i][0] == _T('-')) 00068 { 00069 switch (argv[i][1]) 00070 { 00071 case _T('d'): 00072 pInfo->bResolveAddresses = FALSE; 00073 break; 00074 00075 case _T('h'): 00076 _stscanf(argv[i+1], _T("%d"), &pInfo->iMaxHops); 00077 break; 00078 00079 case _T('j'): 00080 _tprintf(_T("-j is not yet implemented.\n")); 00081 break; 00082 00083 case _T('w'): 00084 _stscanf(argv[i+1], _T("%d"), &pInfo->iTimeOut); 00085 break; 00086 00087 default: 00088 { 00089 _tprintf(_T("%s is not a valid option.\n"), argv[i]); 00090 Usage(); 00091 return FALSE; 00092 } 00093 } 00094 } 00095 else 00096 /* copy target address */ 00097 _tcsncpy(cHostname, argv[i], 255); 00098 } 00099 } 00100 00101 return TRUE; 00102 } 00103 00104 00105 static WORD 00106 CheckSum(PUSHORT data, 00107 UINT size) 00108 { 00109 DWORD dwSum = 0; 00110 00111 while (size > 1) 00112 { 00113 dwSum += *data++; 00114 size -= sizeof(USHORT); 00115 } 00116 00117 if (size) 00118 dwSum += *(UCHAR*)data; 00119 00120 dwSum = (dwSum >> 16) + (dwSum & 0xFFFF); 00121 dwSum += (dwSum >> 16); 00122 00123 return (USHORT)(~dwSum); 00124 } 00125 00126 00127 static VOID 00128 SetupTimingMethod(PAPPINFO pInfo) 00129 { 00130 LARGE_INTEGER PerformanceCounterFrequency; 00131 00132 /* check if performance counters are available */ 00133 pInfo->bUsePerformanceCounter = QueryPerformanceFrequency(&PerformanceCounterFrequency); 00134 00135 if (pInfo->bUsePerformanceCounter) 00136 { 00137 /* restrict execution to first processor on SMP systems */ 00138 if (SetThreadAffinityMask(GetCurrentThread(), 1) == 0) 00139 pInfo->bUsePerformanceCounter = FALSE; 00140 00141 pInfo->TicksPerMs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000; 00142 pInfo->TicksPerUs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000000; 00143 } 00144 else 00145 { 00146 pInfo->TicksPerMs.QuadPart = 1; 00147 pInfo->TicksPerUs.QuadPart = 1; 00148 } 00149 } 00150 00151 00152 static BOOL 00153 ResolveHostname(PAPPINFO pInfo) 00154 { 00155 HOSTENT *hp; 00156 ULONG addr; 00157 00158 ZeroMemory(&pInfo->dest, sizeof(pInfo->dest)); 00159 00160 /* if address is not a dotted decimal */ 00161 if ((addr = inet_addr(cHostname))== INADDR_NONE) 00162 { 00163 if ((hp = gethostbyname(cHostname)) != 0) 00164 { 00165 //CopyMemory(&pInfo->dest.sin_addr, hp->h_addr, hp->h_length); 00166 pInfo->dest.sin_addr = *((struct in_addr *)hp->h_addr); 00167 pInfo->dest.sin_family = hp->h_addrtype; 00168 } 00169 else 00170 { 00171 _tprintf(_T("Unable to resolve target system name %s.\n"), cHostname); 00172 return FALSE; 00173 } 00174 } 00175 else 00176 { 00177 pInfo->dest.sin_addr.s_addr = addr; 00178 pInfo->dest.sin_family = AF_INET; 00179 } 00180 00181 _tcscpy(cDestIP, inet_ntoa(pInfo->dest.sin_addr)); 00182 00183 return TRUE; 00184 } 00185 00186 00187 static LONGLONG 00188 GetTime(PAPPINFO pInfo) 00189 { 00190 LARGE_INTEGER Time; 00191 00192 /* Get the system time using preformance counters if available */ 00193 if (pInfo->bUsePerformanceCounter) 00194 { 00195 if (QueryPerformanceCounter(&Time)) 00196 { 00197 return Time.QuadPart; 00198 } 00199 } 00200 00201 /* otherwise fall back to GetTickCount */ 00202 Time.u.LowPart = (DWORD)GetTickCount(); 00203 Time.u.HighPart = 0; 00204 00205 return (LONGLONG)Time.u.LowPart; 00206 } 00207 00208 00209 static BOOL 00210 SetTTL(SOCKET sock, 00211 INT iTTL) 00212 { 00213 if (setsockopt(sock, 00214 IPPROTO_IP, 00215 IP_TTL, 00216 (const char *)&iTTL, 00217 sizeof(iTTL)) == SOCKET_ERROR) 00218 { 00219 DebugPrint(_T("TTL setsockopt failed : %d. \n"), WSAGetLastError()); 00220 return FALSE; 00221 } 00222 00223 return TRUE; 00224 } 00225 00226 00227 static BOOL 00228 CreateSocket(PAPPINFO pInfo) 00229 { 00230 pInfo->icmpSock = WSASocket(AF_INET, 00231 SOCK_RAW, 00232 IPPROTO_ICMP, 00233 0, 00234 0, 00235 0); 00236 00237 if (pInfo->icmpSock == INVALID_SOCKET) 00238 { 00239 INT err = WSAGetLastError(); 00240 DebugPrint(_T("Could not create socket : %d.\n"), err); 00241 00242 if (err == WSAEACCES) 00243 { 00244 _tprintf(_T("\n\nYou must have access to raw sockets (admin) to run this program!\n\n")); 00245 } 00246 00247 return FALSE; 00248 } 00249 00250 return TRUE; 00251 } 00252 00253 00254 static VOID 00255 PreparePacket(PAPPINFO pInfo, 00256 USHORT iSeqNum) 00257 { 00258 /* assemble ICMP echo request packet */ 00259 pInfo->SendPacket->icmpheader.type = ECHO_REQUEST; 00260 pInfo->SendPacket->icmpheader.code = 0; 00261 pInfo->SendPacket->icmpheader.checksum = 0; 00262 pInfo->SendPacket->icmpheader.id = (USHORT)GetCurrentProcessId(); 00263 pInfo->SendPacket->icmpheader.seq = htons((USHORT)iSeqNum); 00264 00265 /* calculate checksum of packet */ 00266 pInfo->SendPacket->icmpheader.checksum = CheckSum((PUSHORT)&pInfo->SendPacket->icmpheader, 00267 sizeof(ICMP_HEADER) + PACKET_SIZE); 00268 } 00269 00270 00271 static INT 00272 SendPacket(PAPPINFO pInfo) 00273 { 00274 INT iSockRet; 00275 00276 DebugPrint(_T("\nsending packet of %d bytes... "), PACKET_SIZE); 00277 00278 /* get time packet was sent */ 00279 pInfo->lTimeStart = GetTime(pInfo); 00280 00281 iSockRet = sendto(pInfo->icmpSock, //socket 00282 (char *)&pInfo->SendPacket->icmpheader,//buffer 00283 sizeof(ICMP_HEADER) + PACKET_SIZE,//size of buffer 00284 0, //flags 00285 (SOCKADDR *)&pInfo->dest, //destination 00286 sizeof(pInfo->dest)); //address length 00287 00288 if (iSockRet == SOCKET_ERROR) 00289 { 00290 if (WSAGetLastError() == WSAEACCES) 00291 { 00292 /* FIXME: Is this correct? */ 00293 _tprintf(_T("\n\nYou must be an administrator to run this program!\n\n")); 00294 WSACleanup(); 00295 HeapFree(GetProcessHeap(), 0, pInfo); 00296 exit(-1); 00297 } 00298 else 00299 { 00300 DebugPrint(_T("sendto failed %d\n"), WSAGetLastError()); 00301 } 00302 } 00303 else 00304 { 00305 DebugPrint(_T("sent %d bytes\n"), iSockRet); 00306 } 00307 00308 return iSockRet; 00309 } 00310 00311 00312 static BOOL 00313 ReceivePacket(PAPPINFO pInfo) 00314 { 00315 TIMEVAL timeVal; 00316 FD_SET readFDS; 00317 INT iSockRet = 0, iSelRet; 00318 INT iFromLen; 00319 BOOL bRet = FALSE; 00320 00321 iFromLen = sizeof(pInfo->source); 00322 00323 DebugPrint(_T("Receiving packet. Available buffer, %d bytes... "), MAX_PING_PACKET_SIZE); 00324 00325 /* monitor icmpSock for incomming connections */ 00326 FD_ZERO(&readFDS); 00327 FD_SET(pInfo->icmpSock, &readFDS); 00328 00329 /* set timeout values */ 00330 timeVal.tv_sec = pInfo->iTimeOut / 1000; 00331 timeVal.tv_usec = pInfo->iTimeOut % 1000; 00332 00333 iSelRet = select(0, 00334 &readFDS, 00335 NULL, 00336 NULL, 00337 &timeVal); 00338 00339 if (iSelRet == SOCKET_ERROR) 00340 { 00341 DebugPrint(_T("select() failed in sendPacket() %d\n"), WSAGetLastError()); 00342 } 00343 else if (iSelRet == 0) /* if socket timed out */ 00344 { 00345 _tprintf(_T(" * ")); 00346 } 00347 else if ((iSelRet != SOCKET_ERROR) && (iSelRet != 0)) 00348 { 00349 iSockRet = recvfrom(pInfo->icmpSock, // socket 00350 (char *)pInfo->RecvPacket, // buffer 00351 MAX_PING_PACKET_SIZE, // size of buffer 00352 0, // flags 00353 (SOCKADDR *)&pInfo->source, // source address 00354 &iFromLen); // address length 00355 00356 if (iSockRet != SOCKET_ERROR) 00357 { 00358 /* get time packet was recieved */ 00359 pInfo->lTimeEnd = GetTime(pInfo); 00360 DebugPrint(_T("reveived %d bytes\n"), iSockRet); 00361 bRet = TRUE; 00362 } 00363 else 00364 { 00365 DebugPrint(_T("recvfrom failed: %d\n"), WSAGetLastError()); 00366 } 00367 } 00368 00369 return bRet; 00370 } 00371 00372 00373 static INT 00374 DecodeResponse(PAPPINFO pInfo) 00375 { 00376 unsigned short header_len = pInfo->RecvPacket->h_len * 4; 00377 00378 /* cast the recieved packet into an ECHO reply and a TTL Exceed and check the ID*/ 00379 ECHO_REPLY_HEADER *IcmpHdr = (ECHO_REPLY_HEADER *)((char*)pInfo->RecvPacket + header_len); 00380 00381 /* Make sure the reply is ok */ 00382 if (PACKET_SIZE < header_len + ICMP_MIN_SIZE) 00383 { 00384 DebugPrint(_T("too few bytes from %s\n"), inet_ntoa(pInfo->dest.sin_addr)); 00385 return -2; 00386 } 00387 00388 switch (IcmpHdr->icmpheader.type) 00389 { 00390 case TTL_EXCEEDED : 00391 _tprintf(_T("%3ld ms"), (ULONG)((pInfo->lTimeEnd - pInfo->lTimeStart) / pInfo->TicksPerMs.QuadPart)); 00392 return 0; 00393 00394 case ECHO_REPLY : 00395 if (IcmpHdr->icmpheader.id != (USHORT)GetCurrentProcessId()) 00396 { 00397 /* FIXME: our network stack shouldn't allow this... */ 00398 /* we've picked up a packet not related to this process probably from another local program. We ignore it */ 00399 DebugPrint(_T("Rouge packet: header id %d, process id %d"), IcmpHdr->icmpheader.id, GetCurrentProcessId()); 00400 return -1; 00401 } 00402 _tprintf(_T("%3ld ms"), (ULONG)((pInfo->lTimeEnd - pInfo->lTimeStart) / pInfo->TicksPerMs.QuadPart)); 00403 return 1; 00404 00405 case DEST_UNREACHABLE : 00406 _tprintf(_T(" * ")); 00407 return 2; 00408 } 00409 00410 return -3; 00411 } 00412 00413 00414 static BOOL 00415 AllocateBuffers(PAPPINFO pInfo) 00416 { 00417 pInfo->SendPacket = (PECHO_REPLY_HEADER)HeapAlloc(GetProcessHeap(), 00418 0, 00419 sizeof(ECHO_REPLY_HEADER) + PACKET_SIZE); 00420 if (!pInfo->SendPacket) 00421 return FALSE; 00422 00423 pInfo->RecvPacket = (PIPv4_HEADER)HeapAlloc(GetProcessHeap(), 00424 0, 00425 MAX_PING_PACKET_SIZE); 00426 if (!pInfo->RecvPacket) 00427 { 00428 HeapFree(GetProcessHeap(), 00429 0, 00430 pInfo->SendPacket); 00431 00432 return FALSE; 00433 } 00434 00435 return TRUE; 00436 } 00437 00438 00439 static INT 00440 Driver(PAPPINFO pInfo) 00441 { 00442 INT iHopCount = 1; // hop counter. default max is 30 00443 BOOL bFoundTarget = FALSE; // Have we reached our destination yet 00444 INT iRecieveReturn; // RecieveReturn return value 00445 PECHO_REPLY_HEADER icmphdr; 00446 INT iTTL = 1; 00447 00448 INT ret = -1; 00449 00450 //temps for getting host name 00451 CHAR cHost[256]; 00452 CHAR cServ[256]; 00453 CHAR *ip; 00454 00455 SetupTimingMethod(pInfo); 00456 00457 if (AllocateBuffers(pInfo) && 00458 ResolveHostname(pInfo) && 00459 CreateSocket(pInfo)) 00460 { 00461 /* print tracing info to screen */ 00462 _tprintf(_T("\nTracing route to %s [%s]\n"), cHostname, cDestIP); 00463 _tprintf(_T("over a maximum of %d hop"), pInfo->iMaxHops); 00464 pInfo->iMaxHops > 1 ? _tprintf(_T("s:\n\n")) : _tprintf(_T(":\n\n")); 00465 00466 /* run until we hit either max hops, or find the target */ 00467 while ((iHopCount <= pInfo->iMaxHops) && 00468 (bFoundTarget != TRUE)) 00469 { 00470 USHORT iSeqNum = 0; 00471 INT i; 00472 00473 _tprintf(_T("%3d "), iHopCount); 00474 00475 /* run 3 pings for each hop */ 00476 for (i = 0; i < 3; i++) 00477 { 00478 if (SetTTL(pInfo->icmpSock, iTTL) != TRUE) 00479 { 00480 DebugPrint(_T("error in Setup()\n")); 00481 return ret; 00482 } 00483 00484 PreparePacket(pInfo, iSeqNum); 00485 00486 if (SendPacket(pInfo) != SOCKET_ERROR) 00487 { 00488 BOOL bAwaitPacket = FALSE; // indicates whether we have recieved a good packet 00489 00490 do 00491 { 00492 /* Receive replies until we get a successful read, or a fatal error */ 00493 if ((iRecieveReturn = ReceivePacket(pInfo)) < 0) 00494 { 00495 /* FIXME: consider moving this into RecievePacket */ 00496 /* check the seq num in the packet, if it's bad wait for another */ 00497 WORD hdrLen = pInfo->RecvPacket->h_len * 4; 00498 icmphdr = (ECHO_REPLY_HEADER *)((char*)&pInfo->RecvPacket + hdrLen); 00499 if (icmphdr->icmpheader.seq != iSeqNum) 00500 { 00501 _tprintf(_T("bad sequence number!\n")); 00502 continue; 00503 } 00504 } 00505 00506 if (iRecieveReturn) 00507 { 00508 if (DecodeResponse(pInfo) < 0) 00509 bAwaitPacket = TRUE; 00510 } 00511 00512 } while (bAwaitPacket); 00513 } 00514 00515 iSeqNum++; 00516 _tprintf(_T(" ")); 00517 } 00518 00519 if(pInfo->bResolveAddresses) 00520 { 00521 INT iNameInfoRet; // getnameinfo return value 00522 /* gethostbyaddr() and getnameinfo() are 00523 * unimplemented in ROS at present. 00524 * Alex has advised he will be implementing getnameinfo. 00525 * I've used that for the time being for testing in Windows*/ 00526 00527 //ip = inet_addr(inet_ntoa(source.sin_addr)); 00528 //host = gethostbyaddr((char *)&ip, 4, 0); 00529 00530 ip = inet_ntoa(pInfo->source.sin_addr); 00531 00532 iNameInfoRet = getnameinfo((SOCKADDR *)&pInfo->source, 00533 sizeof(SOCKADDR), 00534 cHost, 00535 256, 00536 cServ, 00537 256, 00538 NI_NUMERICSERV); 00539 if (iNameInfoRet == 0) 00540 { 00541 /* if IP address resolved to a hostname, 00542 * print the IP address after it */ 00543 if (lstrcmpA(cHost, ip) != 0) 00544 _tprintf(_T("%s [%s]"), cHost, ip); 00545 else 00546 _tprintf(_T("%s"), cHost); 00547 } 00548 else 00549 { 00550 DebugPrint(_T("error: %d"), WSAGetLastError()); 00551 DebugPrint(_T(" getnameinfo failed: %d"), iNameInfoRet); 00552 _tprintf(_T("%s"), inet_ntoa(pInfo->source.sin_addr)); 00553 } 00554 00555 } 00556 else 00557 _tprintf(_T("%s"), inet_ntoa(pInfo->source.sin_addr)); 00558 00559 _tprintf(_T("\n")); 00560 00561 /* check if we've arrived at the target */ 00562 if (strcmp(cDestIP, inet_ntoa(pInfo->source.sin_addr)) == 0) 00563 bFoundTarget = TRUE; 00564 else 00565 { 00566 iTTL++; 00567 iHopCount++; 00568 Sleep(500); 00569 } 00570 } 00571 _tprintf(_T("\nTrace complete.\n")); 00572 ret = 0; 00573 } 00574 00575 return ret; 00576 } 00577 00578 00579 static VOID 00580 Cleanup(PAPPINFO pInfo) 00581 { 00582 if (pInfo->icmpSock) 00583 closesocket(pInfo->icmpSock); 00584 00585 WSACleanup(); 00586 00587 if (pInfo->SendPacket) 00588 HeapFree(GetProcessHeap(), 00589 0, 00590 pInfo->SendPacket); 00591 00592 if (pInfo->RecvPacket) 00593 HeapFree(GetProcessHeap(), 00594 0, 00595 pInfo->RecvPacket); 00596 } 00597 00598 00599 #if defined(_UNICODE) && defined(__GNUC__) 00600 static 00601 #endif 00602 int _tmain(int argc, LPCTSTR argv[]) 00603 { 00604 PAPPINFO pInfo; 00605 WSADATA wsaData; 00606 int ret = -1; 00607 00608 pInfo = (PAPPINFO)HeapAlloc(GetProcessHeap(), 00609 HEAP_ZERO_MEMORY, 00610 sizeof(APPINFO)); 00611 if (pInfo) 00612 { 00613 pInfo->bResolveAddresses = TRUE; 00614 pInfo->iMaxHops = 30; 00615 pInfo->iTimeOut = 1000; 00616 00617 if (ParseCmdline(argc, argv, pInfo)) 00618 { 00619 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) 00620 { 00621 DebugPrint(_T("WSAStartup failed.\n")); 00622 } 00623 else 00624 { 00625 ret = Driver(pInfo); 00626 Cleanup(pInfo); 00627 } 00628 } 00629 00630 HeapFree(GetProcessHeap(), 00631 0, 00632 pInfo); 00633 } 00634 00635 return ret; 00636 } 00637 00638 00639 #if defined(_UNICODE) && defined(__GNUC__) 00640 /* HACK - MINGW HAS NO OFFICIAL SUPPORT FOR wmain()!!! */ 00641 int main( int argc, char **argv ) 00642 { 00643 WCHAR **argvW; 00644 int i, j, Ret = 1; 00645 00646 if ((argvW = malloc(argc * sizeof(WCHAR*)))) 00647 { 00648 /* convert the arguments */ 00649 for (i = 0, j = 0; i < argc; i++) 00650 { 00651 if (!(argvW[i] = malloc((strlen(argv[i]) + 1) * sizeof(WCHAR)))) 00652 { 00653 j++; 00654 } 00655 swprintf(argvW[i], L"%hs", argv[i]); 00656 } 00657 00658 if (j == 0) 00659 { 00660 /* no error converting the parameters, call wmain() */ 00661 Ret = wmain(argc, (LPCTSTR *)argvW); 00662 } 00663 00664 /* free the arguments */ 00665 for (i = 0; i < argc; i++) 00666 { 00667 if (argvW[i]) 00668 free(argvW[i]); 00669 } 00670 free(argvW); 00671 } 00672 00673 return Ret; 00674 } 00675 #endif Generated on Sat May 26 2012 04:16:15 for ReactOS by
1.7.6.1
|