Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722/* * PROJECT: ReactOS trace route utility * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Trace network paths through networks * COPYRIGHT: Copyright 2018 Ged Murphy <gedmurphy@reactos.org> Copyright 2025 Curtis Wilson <LiquidFox1776@gmail.com> */
#ifdef __REACTOS__#define USE_CONUTILS#define WIN32_NO_STATUS#include <stdarg.h>#include <windef.h>#include <winbase.h>#include <winuser.h>#define _INC_WINDOWS#include <stdlib.h>#include <winsock2.h>#include <conutils.h>#else#include <winsock2.h>#include <Windows.h>#endif#include <ws2tcpip.h>#include <iphlpapi.h>#include <icmpapi.h>#include <strsafe.h>#include <errno.h>#include "resource.h"
#define SIZEOF_ICMP_ERROR 8#define SIZEOF_IO_STATUS_BLOCK 8#define PACKET_SIZE 32#define MAX_IPADDRESS 32#define NUM_OF_PINGS 3#define MIN_HOP_COUNT 1#define MAX_HOP_COUNT 255#define MIN_MILLISECONDS 1#define MAX_MILLISECONDS ULONG_MAX
struct TraceInfo{ bool ResolveAddresses; ULONG MaxHops; ULONG Timeout; WCHAR HostName[NI_MAXHOST]; WCHAR TargetIP[MAX_IPADDRESS]; int Family;
HANDLE hIcmpFile; PADDRINFOW Target;
} Info = { 0 };
#ifndef USE_CONUTILSstaticINTLengthOfStrResource( _In_ HINSTANCE hInst, _In_ UINT uID){ HRSRC hrSrc; HGLOBAL hRes; LPWSTR lpName, lpStr;
if (hInst == NULL) return -1;
lpName = (LPWSTR)MAKEINTRESOURCE((uID >> 4) + 1);
if ((hrSrc = FindResourceW(hInst, lpName, (LPWSTR)RT_STRING)) && (hRes = LoadResource(hInst, hrSrc)) && (lpStr = (WCHAR*)LockResource(hRes))) { UINT x; uID &= 0xF; for (x = 0; x < uID; x++) { lpStr += (*lpStr) + 1; } return (int)(*lpStr); } return -1;}
staticINTAllocAndLoadString( _In_ UINT uID, _Out_ LPWSTR *lpTarget){ HMODULE hInst; INT Length;
hInst = GetModuleHandleW(NULL); Length = LengthOfStrResource(hInst, uID); if (Length++ > 0) { (*lpTarget) = (LPWSTR)LocalAlloc(LMEM_FIXED, Length * sizeof(WCHAR)); if ((*lpTarget) != NULL) { INT Ret; if (!(Ret = LoadStringW(hInst, uID, *lpTarget, Length))) { LocalFree((HLOCAL)(*lpTarget)); } return Ret; } } return 0;}
staticINTOutputText( _In_ UINT uID, ...){ LPWSTR Format; DWORD Ret = 0; va_list lArgs;
if (AllocAndLoadString(uID, &Format) > 0) { va_start(lArgs, uID);
LPWSTR Buffer; Ret = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, Format, 0, 0, (LPWSTR)&Buffer, 0, &lArgs); va_end(lArgs);
if (Ret) { wprintf(Buffer); LocalFree(Buffer); } LocalFree((HLOCAL)Format); }
return Ret;}#else#define OutputText(Id, ...) ConResMsgPrintfEx(StdOut, NULL, 0, Id, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), ##__VA_ARGS__)#endif //USE_CONUTILS
staticVOIDUsage(){ OutputText(IDS_USAGE);}
static boolGetULONG( _In_ PCWSTR String, _Out_ PULONG Value){ PWSTR StopString;
// Check input arguments if (*String == UNICODE_NULL) return false;
// Clear errno so we can use its value after // the call to wcstoul to check for errors. errno = 0;
// Try to convert String to ULONG *Value = wcstoul(String, &StopString, 10); if ((errno != ERANGE) && (errno != 0 || *StopString != UNICODE_NULL)) return false; // The conversion was successful return true;}
static boolResolveTarget(){ ADDRINFOW Hints; ZeroMemory(&Hints, sizeof(Hints)); Hints.ai_family = Info.Family; Hints.ai_flags = AI_CANONNAME;
int Status; Status = GetAddrInfoW(Info.HostName, NULL, &Hints, &Info.Target); if (Status != 0) { return false; }
Status = GetNameInfoW(Info.Target->ai_addr, Info.Target->ai_addrlen, Info.TargetIP, MAX_IPADDRESS, NULL, 0, NI_NUMERICHOST); if (Status != 0) { return false; }
return true;}
static boolPrintHopInfo(_In_ PVOID Buffer){ SOCKADDR_IN6 SockAddrIn6 = { 0 }; SOCKADDR_IN SockAddrIn = { 0 }; PSOCKADDR SockAddr; socklen_t Size;
if (Info.Family == AF_INET6) { PIPV6_ADDRESS_EX Ipv6Addr = (PIPV6_ADDRESS_EX)Buffer; SockAddrIn6.sin6_family = AF_INET6; CopyMemory(SockAddrIn6.sin6_addr.u.Word, Ipv6Addr->sin6_addr, sizeof(SockAddrIn6.sin6_addr)); //SockAddrIn6.sin6_addr = Ipv6Addr->sin6_addr; SockAddr = (PSOCKADDR)&SockAddrIn6; Size = sizeof(SOCKADDR_IN6);
} else { IPAddr *Address = (IPAddr *)Buffer; SockAddrIn.sin_family = AF_INET; SockAddrIn.sin_addr.S_un.S_addr = *Address; SockAddr = (PSOCKADDR)&SockAddrIn; Size = sizeof(SOCKADDR_IN); }
INT Status; bool Resolved = false; WCHAR HostName[NI_MAXHOST]; if (Info.ResolveAddresses) { Status = GetNameInfoW(SockAddr, Size, HostName, NI_MAXHOST, NULL, 0, NI_NAMEREQD); if (Status == 0) { Resolved = true; } }
WCHAR IpAddress[MAX_IPADDRESS]; Status = GetNameInfoW(SockAddr, Size, IpAddress, MAX_IPADDRESS, NULL, 0, NI_NUMERICHOST); if (Status == 0) { if (Resolved) { OutputText(IDS_HOP_RES_INFO, HostName, IpAddress); } else { OutputText(IDS_HOP_IP_INFO, IpAddress); } }
return (Status == 0);}
static ULONGGetResponseStats( _In_ PVOID ReplyBuffer, _Out_ ULONG& RoundTripTime, _Out_ PVOID& AddressInfo){ ULONG Status;
if (Info.Family == AF_INET6) { PICMPV6_ECHO_REPLY EchoReplyV6; EchoReplyV6 = (PICMPV6_ECHO_REPLY)ReplyBuffer; Status = EchoReplyV6->Status; RoundTripTime = EchoReplyV6->RoundTripTime; AddressInfo = &EchoReplyV6->Address; } else {#ifdef _WIN64 PICMP_ECHO_REPLY32 EchoReplyV4; EchoReplyV4 = (PICMP_ECHO_REPLY32)ReplyBuffer;#else PICMP_ECHO_REPLY EchoReplyV4; EchoReplyV4 = (PICMP_ECHO_REPLY)ReplyBuffer;#endif Status = EchoReplyV4->Status; RoundTripTime = EchoReplyV4->RoundTripTime; AddressInfo = &EchoReplyV4->Address; }
return Status;}
static boolDecodeResponse( _In_ PVOID ReplyBuffer, _In_ PVOID LastGoodResponse, _In_ bool OutputHopAddress, _Out_ bool& GoodResponse, _Out_ bool& FoundTarget){ ULONG RoundTripTime; PVOID AddressInfo; ULONG Status = GetResponseStats(ReplyBuffer, RoundTripTime, AddressInfo);
switch (Status) { case IP_SUCCESS: case IP_TTL_EXPIRED_TRANSIT: if (RoundTripTime) { OutputText(IDS_HOP_TIME, RoundTripTime); } else { OutputText(IDS_HOP_ZERO); } GoodResponse = true; break;
case IP_DEST_HOST_UNREACHABLE: case IP_DEST_NET_UNREACHABLE: FoundTarget = true; PrintHopInfo(AddressInfo); OutputText(IDS_HOP_RESPONSE); if (Status == IP_DEST_HOST_UNREACHABLE) { OutputText(IDS_DEST_HOST_UNREACHABLE); } else if (Status == IP_DEST_NET_UNREACHABLE) { OutputText(IDS_DEST_NET_UNREACHABLE); } return true;
case IP_REQ_TIMED_OUT: OutputText(IDS_TIMEOUT); break;
case IP_GENERAL_FAILURE: OutputText(IDS_GEN_FAILURE); return false;
default: OutputText(IDS_TRANSMIT_FAILED, Status); return false; }
if (OutputHopAddress) { if (Status == IP_REQ_TIMED_OUT && LastGoodResponse) { Status = GetResponseStats(LastGoodResponse, RoundTripTime, AddressInfo); } if (Status == IP_SUCCESS) { FoundTarget = true; } if (Status == IP_TTL_EXPIRED_TRANSIT || Status == IP_SUCCESS) { PrintHopInfo(AddressInfo); OutputText(IDS_LINEBREAK); } else if (Status == IP_REQ_TIMED_OUT) { OutputText(IDS_REQ_TIMED_OUT); } }
return true;}
static boolRunTraceRoute(){ bool Success = false; PVOID ReplyBuffer = NULL, LastGoodResponse = NULL; DWORD ReplySize;
HANDLE heap = GetProcessHeap(); bool Quit = false; ULONG HopCount = 1; bool FoundTarget = false;
Success = ResolveTarget(); if (!Success) { OutputText(IDS_UNABLE_RESOLVE, Info.HostName); goto Cleanup; }
ReplySize = PACKET_SIZE + SIZEOF_ICMP_ERROR + SIZEOF_IO_STATUS_BLOCK; if (Info.Family == AF_INET6) { ReplySize += sizeof(ICMPV6_ECHO_REPLY); } else {#ifdef _WIN64 ReplySize += sizeof(ICMP_ECHO_REPLY32);#else ReplySize += sizeof(ICMP_ECHO_REPLY);#endif }
ReplyBuffer = HeapAlloc(heap, HEAP_ZERO_MEMORY, ReplySize); if (ReplyBuffer == NULL) { Success = false; goto Cleanup; }
if (Info.Family == AF_INET6) { Info.hIcmpFile = Icmp6CreateFile(); } else { Info.hIcmpFile = IcmpCreateFile(); } if (Info.hIcmpFile == INVALID_HANDLE_VALUE) { Success = false; goto Cleanup; }
OutputText(IDS_TRACE_INFO, Info.HostName, Info.TargetIP, Info.MaxHops);
IP_OPTION_INFORMATION IpOptionInfo; ZeroMemory(&IpOptionInfo, sizeof(IpOptionInfo));
while ((HopCount <= Info.MaxHops) && (FoundTarget == false) && (Quit == false)) { OutputText(IDS_HOP_COUNT, HopCount);
if (LastGoodResponse) { HeapFree(heap, 0, LastGoodResponse); LastGoodResponse = NULL; }
for (int Ping = 1; Ping <= NUM_OF_PINGS; Ping++) { BYTE SendBuffer[PACKET_SIZE]; bool GoodResponse = false;
IpOptionInfo.Ttl = static_cast<UCHAR>(HopCount);
if (Info.Family == AF_INET6) { struct sockaddr_in6 Source;
ZeroMemory(&Source, sizeof(Source)); Source.sin6_family = AF_INET6;
(void)Icmp6SendEcho2(Info.hIcmpFile, NULL, NULL, NULL, &Source, (struct sockaddr_in6 *)Info.Target->ai_addr, SendBuffer, (USHORT)PACKET_SIZE, &IpOptionInfo, ReplyBuffer, ReplySize, Info.Timeout); } else { (void)IcmpSendEcho2(Info.hIcmpFile, NULL, NULL, NULL, ((PSOCKADDR_IN)Info.Target->ai_addr)->sin_addr.s_addr, SendBuffer, (USHORT)PACKET_SIZE, &IpOptionInfo, ReplyBuffer, ReplySize, Info.Timeout); }
if (DecodeResponse(ReplyBuffer, LastGoodResponse, (Ping == NUM_OF_PINGS), GoodResponse, FoundTarget) == false) { Quit = true; break; }
if (FoundTarget) { Success = true; break; }
if (GoodResponse) { if (LastGoodResponse) { HeapFree(heap, 0, LastGoodResponse); } LastGoodResponse = HeapAlloc(heap, HEAP_ZERO_MEMORY, ReplySize); if (LastGoodResponse == NULL) { Success = false; goto Cleanup; } CopyMemory(LastGoodResponse, ReplyBuffer, ReplySize); } }
HopCount++; Sleep(100); }
OutputText(IDS_TRACE_COMPLETE);
Cleanup: if (ReplyBuffer) { HeapFree(heap, 0, ReplyBuffer); } if (LastGoodResponse) { HeapFree(heap, 0, LastGoodResponse); } if (Info.Target) { FreeAddrInfoW(Info.Target); } if (Info.hIcmpFile) { IcmpCloseHandle(Info.hIcmpFile); }
return Success;}
static boolGetUlongOptionInRange( _In_ int argc, _In_ wchar_t *argv[], _Inout_ int *i, _Out_ ULONG *Value, _In_ ULONG MinimumValue, _In_ ULONG MaximumValue){ ULONG ParsedValue = 0;
// Check if we have enough values if ((*i + 1) > (argc - 1)) { OutputText(IDS_MISSING_OPTION_VALUE, argv[*i]); return false; }
(*i)++;
// Try to parse and convert the value as ULONG. // Check if ParsedValue is within the specified range. if (!GetULONG(argv[*i], &ParsedValue) || ((ParsedValue < MinimumValue) || (ParsedValue > MaximumValue))) { (*i)--; OutputText(IDS_BAD_OPTION_VALUE, argv[*i]); return false; }
*Value = ParsedValue; return true;}
static boolParseCmdline(int argc, wchar_t *argv[]){ if (argc < 2) { Usage(); return false; }
for (int i = 1; i < argc; i++) { if (argv[i][0] == '-' || argv[i][0] == '/') { switch (argv[i][1]) { case 'd': Info.ResolveAddresses = FALSE; break;
case 'h': if (!GetUlongOptionInRange(argc, argv, &i, &Info.MaxHops, MIN_HOP_COUNT, MAX_HOP_COUNT)) { return false; } break;
case 'j': printf("-j is not yet implemented.\n"); return false;
case 'w': if (!GetUlongOptionInRange(argc, argv, &i, &Info.Timeout, MIN_MILLISECONDS, MAX_MILLISECONDS)) { return false; } break;
case '4': Info.Family = AF_INET; break;
case '6': Info.Family = AF_INET6; break;
default: { OutputText(IDS_INVALID_OPTION, argv[i]); Usage(); return false; } } } else { // The host must be the last argument if (i != (argc - 1)) { Usage(); return false; } StringCchCopyW(Info.HostName, NI_MAXHOST, argv[i]); break; } }
// Check for missing host if (Info.HostName[0] == UNICODE_NULL) { OutputText(IDS_MISSING_TARGET); Usage(); return false; } return true;}
EXTERN_Cint wmain(int argc, wchar_t *argv[]){#ifdef USE_CONUTILS /* Initialize the Console Standard Streams */ ConInitStdStreams();#endif
Info.ResolveAddresses = true; Info.MaxHops = 30; Info.Timeout = 4000; Info.Family = AF_UNSPEC;
if (!ParseCmdline(argc, argv)) { return 1; }
WSADATA WsaData; if (WSAStartup(MAKEWORD(2, 2), &WsaData)) { return 1; }
bool Success; Success = RunTraceRoute();
WSACleanup();
return Success ? 0 : 1;}