mirror of
https://github.com/reactos/reactos.git
synced 2025-06-20 07:36:05 +00:00
[TRACERT] Rewrite tracert
- Full rewrite. This commit replaces the old utility. - Use the new ICMP APIs instead of manually crafting ping requests using raw sockets. - Add support for additional languages (the previous utility was hardcoded) - Add support for IPv6 - Make the icmpapi header C++ compatible. (we don't appear to sync this with wine anymore.) - Now runs on Win10, is much more reliable, and brings the code somewhat into the 21st century. (It's currently missing source routing (-j), but as most routers disable this anyway, I'm not sure that it's worth adding)
This commit is contained in:
parent
8d436d9bb5
commit
344f367211
23 changed files with 1164 additions and 749 deletions
566
base/applications/network/tracert/tracert.cpp
Normal file
566
base/applications/network/tracert/tracert.cpp
Normal file
|
@ -0,0 +1,566 @@
|
|||
/*
|
||||
* PROJECT: ReactOS trace route utility
|
||||
* LICENSE: GPL - See COPYING in the top level directory
|
||||
* FILE: base/applications/network/tracert/tracert.cpp
|
||||
* PURPOSE: Trace network paths through networks
|
||||
* COPYRIGHT: Copyright 2018 Ged Murphy <gedmurphy@reactos.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __REACTOS__
|
||||
#define WIN32_NO_STATUS
|
||||
#include <stdarg.h>
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#include <winuser.h>
|
||||
#define _INC_WINDOWS
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <icmpapi.h>
|
||||
#include <strsafe.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
|
||||
|
||||
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_CONUTILS
|
||||
static
|
||||
INT
|
||||
LengthOfStrResource(
|
||||
_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;
|
||||
}
|
||||
|
||||
static
|
||||
INT
|
||||
AllocAndLoadString(
|
||||
_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;
|
||||
}
|
||||
|
||||
static
|
||||
INT
|
||||
OutputText(
|
||||
_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, ...) ConResPrintf(StdOut, Id, __VA_ARGS__)
|
||||
#endif //USE_CONUTILS
|
||||
|
||||
static
|
||||
VOID
|
||||
Usage()
|
||||
{
|
||||
OutputText(IDS_USAGE);
|
||||
}
|
||||
|
||||
static ULONG
|
||||
GetULONG(
|
||||
_In_z_ LPWSTR String
|
||||
)
|
||||
{
|
||||
ULONG Length;
|
||||
Length = wcslen(String);
|
||||
|
||||
ULONG i = 0;
|
||||
while ((i < Length) && ((String[i] < L'0') || (String[i] > L'9'))) i++;
|
||||
if ((i >= Length) || ((String[i] < L'0') || (String[i] > L'9')))
|
||||
{
|
||||
return (ULONG)-1;
|
||||
}
|
||||
|
||||
LPWSTR StopString;
|
||||
return wcstoul(&String[i], &StopString, 10);
|
||||
}
|
||||
|
||||
static bool
|
||||
ResolveTarget()
|
||||
{
|
||||
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 bool
|
||||
PrintHopInfo(_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,
|
||||
NI_MAXHOST,
|
||||
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 bool
|
||||
DecodeResponse(
|
||||
_In_ PVOID ReplyBuffer,
|
||||
_In_ bool OutputHopAddress,
|
||||
_Out_ bool& FoundTarget
|
||||
)
|
||||
{
|
||||
ULONG RoundTripTime;
|
||||
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
|
||||
{
|
||||
PICMP_ECHO_REPLY EchoReplyV4;
|
||||
EchoReplyV4 = (PICMP_ECHO_REPLY)ReplyBuffer;
|
||||
Status = EchoReplyV4->Status;
|
||||
RoundTripTime = EchoReplyV4->RoundTripTime;
|
||||
AddressInfo = &EchoReplyV4->Address;
|
||||
}
|
||||
|
||||
switch (Status)
|
||||
{
|
||||
case IP_SUCCESS:
|
||||
case IP_TTL_EXPIRED_TRANSIT:
|
||||
if (RoundTripTime)
|
||||
{
|
||||
OutputText(IDS_HOP_TIME, RoundTripTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputText(IDS_HOP_ZERO);
|
||||
}
|
||||
break;
|
||||
|
||||
case IP_DEST_HOST_UNREACHABLE:
|
||||
OutputText(IDS_TIMEOUT);
|
||||
break;
|
||||
|
||||
case IP_REQ_TIMED_OUT:
|
||||
OutputText(IDS_TIMEOUT);
|
||||
break;
|
||||
|
||||
case IP_GENERAL_FAILURE:
|
||||
OutputText(IDS_GEN_FAILURE);
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OutputHopAddress)
|
||||
{
|
||||
if (Status == IP_SUCCESS)
|
||||
{
|
||||
FoundTarget = true;
|
||||
}
|
||||
if (Status == IP_TTL_EXPIRED_TRANSIT || Status == IP_SUCCESS)
|
||||
{
|
||||
PrintHopInfo(AddressInfo);
|
||||
}
|
||||
else if (Status == IP_REQ_TIMED_OUT)
|
||||
{
|
||||
OutputText(IDS_REQ_TIMED_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
RunTraceRoute()
|
||||
{
|
||||
bool Success = false;
|
||||
Success = ResolveTarget();
|
||||
if (!Success)
|
||||
{
|
||||
OutputText(IDS_UNABLE_RESOLVE, Info.HostName);
|
||||
return false;
|
||||
}
|
||||
|
||||
BYTE SendBuffer[PACKET_SIZE];
|
||||
ICMPV6_ECHO_REPLY ReplyBufferv6;
|
||||
#ifdef _WIN64
|
||||
ICMP_ECHO_REPLY32 ReplyBufferv432;
|
||||
#else
|
||||
ICMP_ECHO_REPLY ReplyBufferv4;
|
||||
#endif
|
||||
PVOID ReplyBuffer;
|
||||
|
||||
DWORD ReplySize = PACKET_SIZE + SIZEOF_ICMP_ERROR + SIZEOF_IO_STATUS_BLOCK;
|
||||
if (Info.Family == AF_INET6)
|
||||
{
|
||||
ReplyBuffer = &ReplyBufferv6;
|
||||
ReplySize += sizeof(ICMPV6_ECHO_REPLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WIN64
|
||||
ReplyBuffer = &ReplyBufferv432;
|
||||
ReplySize += sizeof(ICMP_ECHO_REPLY32);
|
||||
#else
|
||||
ReplyBuffer = &ReplyBufferv4;
|
||||
ReplySize += sizeof(ICMP_ECHO_REPLY);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (Info.Family == AF_INET6)
|
||||
{
|
||||
Info.hIcmpFile = Icmp6CreateFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
Info.hIcmpFile = IcmpCreateFile();
|
||||
}
|
||||
if (Info.hIcmpFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FreeAddrInfoW(Info.Target);
|
||||
return false;
|
||||
}
|
||||
|
||||
OutputText(IDS_TRACE_INFO, Info.HostName, Info.TargetIP, Info.MaxHops);
|
||||
|
||||
IP_OPTION_INFORMATION IpOptionInfo;
|
||||
ZeroMemory(&IpOptionInfo, sizeof(IpOptionInfo));
|
||||
|
||||
bool Quit = false;
|
||||
ULONG HopCount = 1;
|
||||
bool FoundTarget = false;
|
||||
while ((HopCount <= Info.MaxHops) && (FoundTarget == false) && (Quit == false))
|
||||
{
|
||||
OutputText(IDS_HOP_COUNT, HopCount);
|
||||
|
||||
for (int Ping = 1; Ping <= NUM_OF_PINGS; Ping++)
|
||||
{
|
||||
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, (Ping == NUM_OF_PINGS), FoundTarget) == false)
|
||||
{
|
||||
Quit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (FoundTarget)
|
||||
{
|
||||
Success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HopCount++;
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
OutputText(IDS_TRACE_COMPLETE);
|
||||
|
||||
FreeAddrInfoW(Info.Target);
|
||||
if (Info.hIcmpFile)
|
||||
{
|
||||
IcmpCloseHandle(Info.hIcmpFile);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static bool
|
||||
ParseCmdline(int argc, wchar_t *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
Usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (argv[i][0] == '-')
|
||||
{
|
||||
switch (argv[i][1])
|
||||
{
|
||||
case 'd':
|
||||
Info.ResolveAddresses = FALSE;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
Info.MaxHops = GetULONG(argv[++i]);
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
printf("-j is not yet implemented.\n");
|
||||
return false;
|
||||
|
||||
case 'w':
|
||||
Info.Timeout = GetULONG(argv[++i]);
|
||||
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
|
||||
{
|
||||
StringCchCopyW(Info.HostName, NI_MAXHOST, argv[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EXTERN_C
|
||||
int wmain(int argc, wchar_t *argv[])
|
||||
{
|
||||
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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue