windows-nt/Source/XPSP1/NT/net/tcpip/commands/tracert/tracert.c

877 lines
27 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991-2000 Microsoft Corporation
Module Name:
tracert.c
Abstract:
TraceRoute utility for TCP/IP.
Author:
Numerous TCP/IP folks.
Revision History:
Who When What
-------- -------- ----------------------------------------------
Notes:
--*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <snmp.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ntddip6.h>
#include <winnlsp.h>
#include <iphlpapi.h>
#include "llinfo.h"
#include "tcpcmd.h"
#include "ipexport.h"
#include "icmpapi.h"
#include "nlstxt.h"
#define DEFAULT_MAXIMUM_HOPS 30
#define DEFAULT_TOS 0
#define DEFAULT_FLAGS 0
#define DEFAULT_SEND_SIZE 64
#define DEFAULT_RECEIVE_SIZE ( (sizeof(ICMP_ECHO_REPLY) + \
DEFAULT_SEND_SIZE + \
MAX_OPT_SIZE))
#define DEFAULT_TOS 0
#define DEFAULT_TIMEOUT 4000L
#define MIN_INTERVAL 1000L
#define STDOUT 1
char SendBuffer[DEFAULT_SEND_SIZE];
char RcvBuffer[DEFAULT_RECEIVE_SIZE];
WSADATA WsaData;
struct IPErrorTable {
IP_STATUS Error; // The IP Error
DWORD ErrorNlsID; // The corresponding NLS string ID.
} ErrorTable[] =
{
{ IP_BUF_TOO_SMALL, TRACERT_BUF_TOO_SMALL },
{ IP_DEST_NET_UNREACHABLE, TRACERT_DEST_NET_UNREACHABLE },
{ IP_DEST_HOST_UNREACHABLE, TRACERT_DEST_HOST_UNREACHABLE },
{ IP_DEST_PROT_UNREACHABLE, TRACERT_DEST_PROT_UNREACHABLE },
{ IP_DEST_PORT_UNREACHABLE, TRACERT_DEST_PORT_UNREACHABLE },
{ IP_NO_RESOURCES, TRACERT_NO_RESOURCES },
{ IP_BAD_OPTION, TRACERT_BAD_OPTION },
{ IP_HW_ERROR, TRACERT_HW_ERROR },
{ IP_PACKET_TOO_BIG, TRACERT_PACKET_TOO_BIG },
{ IP_REQ_TIMED_OUT, TRACERT_REQ_TIMED_OUT },
{ IP_BAD_REQ, TRACERT_BAD_REQ },
{ IP_BAD_ROUTE, TRACERT_BAD_ROUTE },
{ IP_TTL_EXPIRED_TRANSIT, TRACERT_TTL_EXPIRED_TRANSIT },
{ IP_TTL_EXPIRED_REASSEM, TRACERT_TTL_EXPIRED_REASSEM },
{ IP_PARAM_PROBLEM, TRACERT_PARAM_PROBLEM },
{ IP_SOURCE_QUENCH, TRACERT_SOURCE_QUENCH },
{ IP_OPTION_TOO_BIG, TRACERT_OPTION_TOO_BIG },
{ IP_BAD_DESTINATION, TRACERT_BAD_DESTINATION },
{ IP_NEGOTIATING_IPSEC, TRACERT_NEGOTIATING_IPSEC },
{ IP_GENERAL_FAILURE, TRACERT_GENERAL_FAILURE }
};
PWCHAR
GetErrorString(int ErrorCode)
{
DWORD Status;
DWORD Length;
static WCHAR ErrorString[2048]; // a 2K static buffer should suffice
Length = 2048;
Status = GetIpErrorString(ErrorCode, ErrorString, &Length);
if (Status == NO_ERROR) {
return ErrorString; // success
}
return L""; // return a null string
}
unsigned
NlsPutMsg(unsigned Handle, unsigned usMsgNum, ... )
{
unsigned msglen;
VOID * vp;
va_list arglist;
DWORD StrLen;
va_start(arglist, usMsgNum);
if (!(msglen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE,
NULL,
usMsgNum,
0L, // Default country ID.
(LPTSTR)&vp,
0,
&arglist)))
return(0);
// Convert vp to oem
StrLen=strlen(vp);
CharToOemBuff((LPCTSTR)vp,(LPSTR)vp,StrLen);
msglen = _write(Handle, vp, StrLen);
LocalFree(vp);
return(msglen);
}
unsigned long
str2ip(char *addr)
{
char *endptr;
int i; // Counter variable.
unsigned long curaddr = 0;
unsigned long temp;
for (i = 0; i < 4; i++) {
temp = strtoul(addr, &endptr, 10);
if (temp > 255)
return 0L;
if (endptr[0] != '.')
if (i != 3)
return 0L;
else
if (endptr[0] != '\0' && endptr[0] != ' ')
return 0L;
addr = endptr+1;
curaddr = (curaddr << 8) + temp;
}
return net_long(curaddr);
}
void
print_addr(SOCKADDR *sa, socklen_t salen, BOOLEAN DoReverseLookup)
{
char hostname[NI_MAXHOST];
int i;
BOOLEAN didReverse = FALSE;
if (DoReverseLookup) {
i = getnameinfo(sa, salen, hostname, sizeof(hostname),
NULL, 0, NI_NAMEREQD);
if (i == NO_ERROR) {
didReverse = TRUE;
NlsPutMsg(STDOUT, TRACERT_TARGET_NAME, hostname);
}
}
i = getnameinfo(sa, salen, hostname, sizeof(hostname),
NULL, 0, NI_NUMERICHOST);
if (i != NO_ERROR) {
// This should never happen unless there is a memory problem,
// in which case the message associated with TRACERT_NO_RESOURCES
// is reasonable.
NlsPutMsg(STDOUT, TRACERT_NO_RESOURCES);
exit (1);
}
if (didReverse) {
NlsPutMsg( STDOUT, TRACERT_BRKT_IP_ADDRESS, hostname );
} else {
NlsPutMsg( STDOUT, TRACERT_IP_ADDRESS, hostname );
}
}
void
print_ip_addr(IPAddr ipv4Addr, BOOLEAN DoReverseLookup)
{
SOCKADDR_IN sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ipv4Addr;
print_addr((LPSOCKADDR)&sin, sizeof(sin), DoReverseLookup);
}
void
print_ipv6_addr(IN6_ADDR *ipv6Addr, BOOLEAN DoReverseLookup)
{
SOCKADDR_IN6 sin;
memset(&sin, 0, sizeof(sin));
sin.sin6_family = AF_INET6;
memcpy(&sin.sin6_addr, ipv6Addr, sizeof(sin.sin6_addr));
print_addr((LPSOCKADDR)&sin, sizeof(sin), DoReverseLookup);
}
void
print_time(ulong Time)
{
if (Time) {
NlsPutMsg( STDOUT, TRACERT_TIME, Time );
}
else {
NlsPutMsg( STDOUT, TRACERT_TIME_10MS );
}
}
BOOLEAN
param(
ulong *parameter,
char **argv,
int argc,
int current,
ulong min,
ulong max
)
{
ulong temp;
char *dummy;
if (current == (argc - 1) ) {
NlsPutMsg( STDOUT, TRACERT_NO_OPTION_VALUE, argv[current] );
return(FALSE);
}
temp = strtoul(argv[current+1], &dummy, 0);
if (temp < min || temp > max) {
NlsPutMsg( STDOUT, TRACERT_BAD_OPTION_VALUE, argv[current] );
return(FALSE);
}
*parameter = temp;
return(TRUE);
}
BOOLEAN
ResolveTarget(
int Family,
char *TargetString,
SOCKADDR *TargetAddress,
socklen_t *TargetAddressLen,
char *TargetName,
int TargetNameLen,
BOOLEAN DoReverseLookup
)
{
int i;
struct addrinfo hints, *ai;
TargetName[0] = '\0';
memset(&hints, 0, sizeof(hints));
hints.ai_family = Family;
hints.ai_flags = AI_NUMERICHOST;
i = getaddrinfo(TargetString, NULL, &hints, &ai);
if (i == NO_ERROR) {
*TargetAddressLen = ai->ai_addrlen;
memcpy(TargetAddress, ai->ai_addr, ai->ai_addrlen);
if (DoReverseLookup) {
getnameinfo(ai->ai_addr, ai->ai_addrlen,
TargetName, TargetNameLen,
NULL, 0, NI_NAMEREQD);
}
freeaddrinfo(ai);
return(TRUE);
} else {
hints.ai_flags = AI_CANONNAME;
if (getaddrinfo(TargetString, NULL, &hints, &ai) == 0) {
*TargetAddressLen = ai->ai_addrlen;
memcpy(TargetAddress, ai->ai_addr, ai->ai_addrlen);
strcpy(TargetName,
(ai->ai_canonname)? ai->ai_canonname : TargetString);
freeaddrinfo(ai);
return(TRUE);
}
}
return(FALSE);
} // ResolveTarget
int
GetSource(int family, char *astr, struct sockaddr *address)
{
struct addrinfo hints;
struct addrinfo *result;
memset(&hints, 0, sizeof hints);
hints.ai_flags = AI_PASSIVE;
hints.ai_family = family;
if (getaddrinfo(astr, NULL, &hints, &result) != 0)
return FALSE;
RtlCopyMemory(address, result->ai_addr, result->ai_addrlen);
return TRUE;
}
BOOLEAN
SetFamily(DWORD *Family, DWORD Value, char *arg)
{
if ((*Family != AF_UNSPEC) && (*Family != Value)) {
NlsPutMsg(STDOUT, TRACERT_INVALID_SWITCH, arg);
// NlsPutMsg(STDOUT, TRACERT_FAMILY, arg,
// (Value==AF_INET)? "IPv4" : "IPv6");
return FALSE;
}
*Family = Value;
return TRUE;
}
int __cdecl
main(int argc, char **argv)
{
SOCKADDR_STORAGE address, sourceAddress;
socklen_t addressLen;
IPAddr reply4Address;
IN6_ADDR reply6Address;
DWORD numberOfReplies;
HANDLE IcmpHandle;
DWORD status;
PICMP_ECHO_REPLY reply4;
PICMPV6_ECHO_REPLY reply6;
char hostname[NI_MAXHOST], literal[INET6_ADDRSTRLEN];
char *arg;
int i;
ulong maximumHops = DEFAULT_MAXIMUM_HOPS;
BOOLEAN doReverseLookup = TRUE;
IP_OPTION_INFORMATION options;
char optionsData[MAX_OPT_SIZE];
char *optionPtr;
uchar currentIndex;
IPAddr tempAddr;
uchar j;
uchar SRIndex = 0;
ulong timeout = DEFAULT_TIMEOUT;
BOOLEAN foundAddress = FALSE;
BOOLEAN haveReply;
DWORD Family = AF_UNSPEC;
//
// This will ensure the correct language message is displayed when
// NlsPutMsg is called.
//
SetThreadUILanguage(0);
if (WSAStartup(MAKEWORD(2, 0), &WsaData)) {
NlsPutMsg(STDOUT, TRACERT_WSASTARTUP_FAILED, GetLastError());
return(1);
}
memset(&sourceAddress, 0, sizeof sourceAddress);
options.Ttl = 1;
options.Tos = DEFAULT_TOS;
options.Flags = DEFAULT_FLAGS;
options.OptionsSize = 0;
options.OptionsData = optionsData;
if (argc < 2) {
NlsPutMsg( STDOUT, TRACERT_USAGE, argv[0] );
goto error_exit;
}
//
// process command line
//
for (i=1; i < argc; i++) {
arg = argv[i];
if ((arg[0] == '-') || (arg[0] == '/')) {
switch(arg[1]) {
case '?':
NlsPutMsg(STDOUT, TRACERT_USAGE, argv[0]);
goto error_exit;
case 'd':
doReverseLookup = FALSE;
break;
case 'h':
if (!param(&maximumHops, argv, argc, i++, 1, 255)) {
goto error_exit;
}
break;
case 'j': // Loose source routing
// Only implemented for IPv4 so far
if (!SetFamily(&Family, AF_INET, arg)) {
goto error_exit;
}
currentIndex = options.OptionsSize;
if ((currentIndex + 7) > MAX_OPT_SIZE) {
NlsPutMsg(STDOUT, TRACERT_TOO_MANY_OPTIONS);
goto error_exit;
}
optionPtr = options.OptionsData;
optionPtr[currentIndex] = (char) IP_OPT_LSRR;
optionPtr[currentIndex+1] = 3;
optionPtr[currentIndex+2] = 4; // Set initial pointer value
options.OptionsSize += 3;
while ( (i < (argc - 2)) && (*argv[i+1] != '-')) {
if ((currentIndex + 7) > MAX_OPT_SIZE) {
NlsPutMsg(STDOUT, TRACERT_TOO_MANY_OPTIONS);
goto error_exit;
}
arg = argv[++i];
tempAddr = inet_addr(arg);
if (tempAddr == INADDR_NONE) {
NlsPutMsg(
STDOUT,
TRACERT_BAD_ROUTE_ADDRESS,
arg
);
goto error_exit;
}
j = optionPtr[currentIndex+1];
*(ulong UNALIGNED *)&optionPtr[j+currentIndex] = tempAddr;
optionPtr[currentIndex+1] += 4;
options.OptionsSize += 4;
}
SRIndex = optionPtr[currentIndex+1] + currentIndex;
optionPtr[currentIndex+1] += 4; // Save space for dest. addr
options.OptionsSize += 4;
break;
case 'w':
if (!param(&timeout, argv, argc, i++, 1, 0xffffffff)) {
goto error_exit;
}
break;
case 'R':
// Only implemented for IPv6 so far
if (!SetFamily(&Family, AF_INET6, arg)) {
goto error_exit;
}
options.Flags |= ICMPV6_ECHO_REQUEST_FLAG_REVERSE;
break;
case 'S':
// Only implemented for IPv6 so far
if (!SetFamily(&Family, AF_INET6, arg)) {
goto error_exit;
}
if (!GetSource(Family, argv[++i], (LPSOCKADDR)&sourceAddress)) {
NlsPutMsg(STDOUT, TRACERT_BAD_OPTION_VALUE, arg[1]);
// NlsPutMsg(STDOUT, TRACERT_BAD_ADDRESS, argv[i]);
goto error_exit;
}
break;
case '4':
if (!SetFamily(&Family, AF_INET, arg)) {
goto error_exit;
}
break;
case '6':
if (!SetFamily(&Family, AF_INET6, arg)) {
goto error_exit;
}
break;
default:
NlsPutMsg(STDOUT, TRACERT_INVALID_SWITCH, argv[i]);
NlsPutMsg(STDOUT, TRACERT_USAGE);
goto error_exit;
break;
}
}
else {
foundAddress = TRUE;
if (!ResolveTarget(Family, argv[i], (LPSOCKADDR)&address,
&addressLen, hostname, sizeof(hostname),
doReverseLookup)) {
NlsPutMsg( STDOUT, TRACERT_MESSAGE_1, argv[i] );
goto error_exit;
}
}
}
if (!foundAddress) {
NlsPutMsg(STDOUT, TRACERT_NO_ADDRESS);
NlsPutMsg(STDOUT, TRACERT_USAGE);
goto error_exit;
}
Family = address.ss_family;
if (Family == AF_INET) {
if (SRIndex != 0) {
*(ulong UNALIGNED *)&options.OptionsData[SRIndex] = ((LPSOCKADDR_IN)&address)->sin_addr.s_addr;
}
IcmpHandle = IcmpCreateFile();
} else {
if (sourceAddress.ss_family == AF_UNSPEC) {
SOCKET s;
DWORD BytesReturned;
//
// A source address was not specified.
// Get the preferred source address for this destination.
//
// If you want each individual echo request
// to select a source address, use "-S ::".
//
s = socket(address.ss_family, 0, 0);
if (s == INVALID_SOCKET) {
NlsPutMsg(STDOUT, TRACERT_WSASTARTUP_FAILED, WSAGetLastError());
// NlsPutMsg(STDOUT, TRACERT_SOCKET_FAILED, WSAGetLastError());
exit(1);
}
(void) WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY,
&address, sizeof address,
&sourceAddress, sizeof sourceAddress,
&BytesReturned, NULL, NULL);
closesocket(s);
}
IcmpHandle = Icmp6CreateFile();
}
if (IcmpHandle == INVALID_HANDLE_VALUE) {
status = GetLastError();
NlsPutMsg( STDOUT, TRACERT_MESSAGE_2, status );
goto error_exit;
}
getnameinfo((LPSOCKADDR)&address, addressLen, literal, sizeof(literal),
NULL, 0, NI_NUMERICHOST);
if (hostname[0]) {
NlsPutMsg(
STDOUT,
TRACERT_HEADER1,
hostname,
literal,
maximumHops
);
}
else {
NlsPutMsg(
STDOUT,
TRACERT_HEADER2,
literal,
maximumHops
);
}
while((options.Ttl <= maximumHops) && (options.Ttl != 0)) {
NlsPutMsg( STDOUT, TRACERT_MESSAGE_4, (uint)options.Ttl );
haveReply = FALSE;
for (i=0; i<3; i++) {
BOOLEAN ErrorNotHandled = FALSE;
if (Family == AF_INET) {
numberOfReplies = IcmpSendEcho2(
IcmpHandle,
0,
NULL,
NULL,
((LPSOCKADDR_IN)&address)->sin_addr.s_addr,
SendBuffer,
DEFAULT_SEND_SIZE,
&options,
RcvBuffer,
DEFAULT_RECEIVE_SIZE,
timeout
);
if (numberOfReplies == 0) {
// We did not get any replies. This is possibly a timeout,
// or an internal error to IP.
//
status = GetLastError();
reply4 = NULL;
if (status == IP_REQ_TIMED_OUT) {
NlsPutMsg(STDOUT, TRACERT_NO_REPLY_TIME);
if (i == 2) {
if (haveReply) {
print_ip_addr(
reply4Address,
doReverseLookup
);
NlsPutMsg(STDOUT, TRACERT_CR);
}
else {
NlsPutMsg( STDOUT, TRACERT_REQ_TIMED_OUT);
}
}
}
else {
ErrorNotHandled = TRUE;
}
}
else {
// We got a reply. It's either for the final destination
// (IP_SUCCESS), or because the TTL expired at a node along
// the way, or we got an unexpected error response.
//
reply4 = (PICMP_ECHO_REPLY) RcvBuffer;
status = reply4->Status;
if (status == IP_SUCCESS) {
print_time(reply4->RoundTripTime);
if (i == 2) {
print_ip_addr(
reply4->Address,
doReverseLookup
);
NlsPutMsg(STDOUT, TRACERT_CR);
goto loop_end;
}
else {
haveReply = TRUE;
reply4Address = reply4->Address;
}
}
else if (status == IP_TTL_EXPIRED_TRANSIT) {
print_time(reply4->RoundTripTime);
if (i == 2) {
print_ip_addr(
reply4->Address,
doReverseLookup
);
NlsPutMsg(STDOUT, TRACERT_CR);
if (reply4->RoundTripTime < MIN_INTERVAL) {
Sleep(MIN_INTERVAL - reply4->RoundTripTime);
}
}
else {
haveReply = TRUE;
reply4Address = reply4->Address;
}
}
else {
ErrorNotHandled = TRUE;
}
}
// If we've not handled the status code by now, it represents
// an unexpected fatal error and we'll now bail out.
//
if (ErrorNotHandled) {
if (status < IP_STATUS_BASE) {
NlsPutMsg( STDOUT, TRACERT_MESSAGE_7, status );
}
else {
if (reply4 != NULL) {
print_ip_addr(
reply4->Address,
doReverseLookup
);
NlsPutMsg( STDOUT, TRACERT_MESSAGE_6 );
}
for (i = 0;
( ErrorTable[i].Error != status &&
ErrorTable[i].Error != IP_GENERAL_FAILURE
);
i++
);
NlsPutMsg( STDOUT, ErrorTable[i].ErrorNlsID );
}
goto loop_end;
}
}
else { // AF_INET6
numberOfReplies = Icmp6SendEcho2(
IcmpHandle,
0,
NULL,
NULL,
(LPSOCKADDR_IN6)&sourceAddress,
(LPSOCKADDR_IN6)&address,
SendBuffer,
DEFAULT_SEND_SIZE,
&options,
RcvBuffer,
DEFAULT_RECEIVE_SIZE,
timeout
);
if (numberOfReplies == 0) {
// We did not get any replies. This is possibly a timeout,
// or an internal error to IP.
//
status = GetLastError();
reply6 = NULL;
if (status == IP_REQ_TIMED_OUT) {
NlsPutMsg(STDOUT, TRACERT_NO_REPLY_TIME);
if (i == 2) {
if (haveReply) {
print_ipv6_addr(
&reply6Address,
doReverseLookup
);
NlsPutMsg(STDOUT, TRACERT_CR);
}
else {
NlsPutMsg( STDOUT, TRACERT_REQ_TIMED_OUT);
}
}
}
else {
ErrorNotHandled = TRUE;
}
}
else {
// We got a reply. It's either for the final destination
// (IP_SUCCESS), or because the TTL expired at a node along
// the way, or we got an unexpected error response.
//
reply6 = (PICMPV6_ECHO_REPLY) RcvBuffer;
status = reply6->Status;
if (status == IP_SUCCESS) {
print_time(reply6->RoundTripTime);
if (i == 2) {
print_ipv6_addr(
(IN6_ADDR*)&reply6->Address.sin6_addr,
doReverseLookup
);
NlsPutMsg(STDOUT, TRACERT_CR);
goto loop_end;
}
else {
haveReply = TRUE;
RtlCopyMemory(&reply6Address,
&reply6->Address.sin6_addr,
sizeof(IN6_ADDR));
}
}
else if (status == IP_HOP_LIMIT_EXCEEDED) {
print_time(reply6->RoundTripTime);
if (i == 2) {
print_ipv6_addr(
(IN6_ADDR*)&reply6->Address.sin6_addr,
doReverseLookup
);
NlsPutMsg(STDOUT, TRACERT_CR);
if (reply6->RoundTripTime < MIN_INTERVAL) {
Sleep(MIN_INTERVAL - reply6->RoundTripTime);
}
}
else {
haveReply = TRUE;
RtlCopyMemory(&reply6Address,
&reply6->Address.sin6_addr,
sizeof(IN6_ADDR));
}
}
else {
ErrorNotHandled = TRUE;
}
}
// If we've not handled the status code by now, it represents
// an unexpected fatal error and we'll now bail out.
//
if (ErrorNotHandled) {
if (status < IP_STATUS_BASE) {
NlsPutMsg( STDOUT, TRACERT_MESSAGE_7, status );
}
else {
if (reply6 != NULL) {
print_ipv6_addr(
(IN6_ADDR*)&reply6->Address.sin6_addr,
doReverseLookup
);
NlsPutMsg( STDOUT, TRACERT_MESSAGE_6 );
}
for (i = 0;
( ErrorTable[i].Error != status &&
ErrorTable[i].Error != IP_GENERAL_FAILURE
);
i++
);
NlsPutMsg( STDOUT, ErrorTable[i].ErrorNlsID );
}
goto loop_end;
}
}
}
options.Ttl++;
}
loop_end:
NlsPutMsg( STDOUT, TRACERT_MESSAGE_8 );
IcmpCloseHandle(IcmpHandle);
WSACleanup();
return(0);
error_exit:
WSACleanup();
return(1);
}