535 lines
14 KiB
C
535 lines
14 KiB
C
|
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
||
|
//
|
||
|
// Copyright (c) 1985-2000 Microsoft Corporation
|
||
|
//
|
||
|
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
|
||
|
// You should have received a copy of the Microsoft End-User License Agreement
|
||
|
// for this software along with this release; see the file "license.txt".
|
||
|
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
|
||
|
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
|
||
|
//
|
||
|
// Abstract:
|
||
|
//
|
||
|
// TraceRoute utility for IPv6.
|
||
|
//
|
||
|
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <devioctl.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <malloc.h>
|
||
|
#include <winsock2.h>
|
||
|
#include <ws2tcpip.h>
|
||
|
#include <wspiapi.h>
|
||
|
// Need ntddip6 before ws2ip6 to get CopyTDIFromSA6 and CopySAFromTDI6.
|
||
|
#include <ntddip6.h>
|
||
|
#include <ws2ip6.h>
|
||
|
|
||
|
//
|
||
|
// Localization library and MessageIds.
|
||
|
//
|
||
|
#include <iphlpapi.h>
|
||
|
#include <nls.h>
|
||
|
#include "localmsg.h"
|
||
|
|
||
|
#define DEFAULT_MAXIMUM_HOPS 30
|
||
|
#define DEFAULT_SEND_SIZE 64
|
||
|
|
||
|
#define DEFAULT_TIMEOUT 3000L
|
||
|
#define MIN_INTERVAL 1000L
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
format_addr(struct sockaddr_in6 *address)
|
||
|
{
|
||
|
static char buffer[128];
|
||
|
|
||
|
if (getnameinfo((struct sockaddr *)address, sizeof *address,
|
||
|
buffer, sizeof buffer, NULL, 0, NI_NUMERICHOST) != 0)
|
||
|
strcpy(buffer, "<invalid>");
|
||
|
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
print_ip_addr(struct sockaddr_in6 *addr, BOOLEAN DoReverseLookup)
|
||
|
{
|
||
|
char stringBuffer[128];
|
||
|
BOOLEAN didReverse = FALSE;
|
||
|
|
||
|
if (DoReverseLookup) {
|
||
|
if (getnameinfo((struct sockaddr *)addr, sizeof *addr,
|
||
|
stringBuffer, sizeof stringBuffer,
|
||
|
NULL, 0, NI_NAMEREQD) == 0) {
|
||
|
didReverse = TRUE;
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_0, stringBuffer);
|
||
|
// printf("%s ", stringBuffer);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (getnameinfo((struct sockaddr *)addr, sizeof *addr,
|
||
|
stringBuffer, sizeof stringBuffer,
|
||
|
NULL, 0, NI_NUMERICHOST) != 0)
|
||
|
strcpy(stringBuffer, "<invalid>");
|
||
|
|
||
|
if (didReverse) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_1, stringBuffer);
|
||
|
// printf("[%s]", stringBuffer);
|
||
|
|
||
|
} else {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_2, stringBuffer);
|
||
|
// printf("%s", stringBuffer);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
print_time(u_long Time)
|
||
|
{
|
||
|
if (Time) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_3, Time);
|
||
|
// printf("%4u ms ", Time);
|
||
|
|
||
|
} else {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_4);
|
||
|
// printf(" <1 ms ");
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
param(
|
||
|
u_long *parameter,
|
||
|
char **argv,
|
||
|
int argc,
|
||
|
int current,
|
||
|
u_long min,
|
||
|
u_long max)
|
||
|
{
|
||
|
u_long temp;
|
||
|
char *dummy;
|
||
|
|
||
|
if (current == (argc - 1)) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_5, argv[current]);
|
||
|
// printf("A value must be supplied for option %s.\n", argv[current]);
|
||
|
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
temp = strtoul(argv[current+1], &dummy, 0);
|
||
|
if (temp < min || temp > max) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_6, argv[current]);
|
||
|
// printf("Bad value for option %s.\n", argv[current]);
|
||
|
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
*parameter = temp;
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Can only be called once, because
|
||
|
// a) does not call freeaddrinfo
|
||
|
// b) uses a static buffer for some results
|
||
|
//
|
||
|
int
|
||
|
get_pingee(char *ahstr, int dnsreq, struct sockaddr_in6 *address, char **hstr)
|
||
|
{
|
||
|
struct addrinfo hints;
|
||
|
struct addrinfo *result;
|
||
|
char *name = NULL;
|
||
|
|
||
|
memset(&hints, 0, sizeof hints);
|
||
|
hints.ai_flags = AI_NUMERICHOST;
|
||
|
hints.ai_family = PF_INET6;
|
||
|
|
||
|
if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
|
||
|
//
|
||
|
// Not a numeric address.
|
||
|
// Try again with DNS name resolution.
|
||
|
//
|
||
|
hints.ai_flags = AI_CANONNAME;
|
||
|
if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
|
||
|
//
|
||
|
// Failure - we can not resolve the name.
|
||
|
//
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
name = result->ai_canonname;
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// Should we do a reverse-lookup to get a name?
|
||
|
//
|
||
|
if (dnsreq) {
|
||
|
static char namebuf[NI_MAXHOST];
|
||
|
|
||
|
if (getnameinfo(result->ai_addr, result->ai_addrlen,
|
||
|
namebuf, sizeof namebuf,
|
||
|
NULL, 0,
|
||
|
NI_NAMEREQD) == 0) {
|
||
|
//
|
||
|
// Reverse lookup succeeded.
|
||
|
//
|
||
|
name = namebuf;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*address = * (struct sockaddr_in6 *) result->ai_addr;
|
||
|
*hstr = name;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
get_source(char *astr, struct sockaddr_in6 *address)
|
||
|
{
|
||
|
struct addrinfo hints;
|
||
|
struct addrinfo *result;
|
||
|
|
||
|
memset(&hints, 0, sizeof hints);
|
||
|
hints.ai_flags = AI_PASSIVE;
|
||
|
hints.ai_family = PF_INET6;
|
||
|
|
||
|
if (getaddrinfo(astr, NULL, &hints, &result) != 0)
|
||
|
return FALSE;
|
||
|
|
||
|
*address = * (struct sockaddr_in6 *) result->ai_addr;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
PrintUsage(void)
|
||
|
{
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_7);
|
||
|
// printf("\nUsage: tracert6 [-d] [-h maximum_hops] [-w timeout] "
|
||
|
// "[-s srcaddr] target_name\n\n"
|
||
|
// "Options:\n"
|
||
|
// "-d Do not resolve addresses to hostnames.\n"
|
||
|
// "-h max_hops Maximum number of hops to search for target.\n"
|
||
|
// "-w timeout Wait timeout milliseconds for each reply.\n"
|
||
|
// "-s srcaddr Source address to use.\n"
|
||
|
// "-r Use routing header to test reverse route also.\n");
|
||
|
|
||
|
}
|
||
|
|
||
|
int __cdecl
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
struct sockaddr_in6 address, sourceAddress, replyAddress;
|
||
|
u_short RcvSize;
|
||
|
BOOL result;
|
||
|
DWORD bytesReturned;
|
||
|
HANDLE Handle;
|
||
|
DWORD status;
|
||
|
PICMPV6_ECHO_REQUEST request;
|
||
|
PICMPV6_ECHO_REPLY reply;
|
||
|
char *hostname;
|
||
|
char *arg;
|
||
|
int i;
|
||
|
u_long maximumHops = DEFAULT_MAXIMUM_HOPS;
|
||
|
BOOLEAN doReverseLookup = TRUE;
|
||
|
u_long timeout = DEFAULT_TIMEOUT;
|
||
|
BOOLEAN foundAddress = FALSE;
|
||
|
BOOLEAN haveReply;
|
||
|
u_char Ttl = 1;
|
||
|
WSADATA WsaData;
|
||
|
int Reverse = FALSE;
|
||
|
|
||
|
PWCHAR Message;
|
||
|
|
||
|
memset(&address, 0, sizeof address);
|
||
|
memset(&sourceAddress, 0, sizeof sourceAddress);
|
||
|
|
||
|
request = alloca(sizeof *request + DEFAULT_SEND_SIZE);
|
||
|
reply = alloca(sizeof *reply + DEFAULT_SEND_SIZE);
|
||
|
|
||
|
if (WSAStartup( MAKEWORD(2, 0), &WsaData)) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_8, GetLastError());
|
||
|
// printf("Unable to initialize the Windows Sockets interface, "
|
||
|
// "error code %u.\n", GetLastError());
|
||
|
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
if (argc < 2) {
|
||
|
PrintUsage();
|
||
|
goto error_exit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// process command line
|
||
|
//
|
||
|
for (i=1; i < argc; i++) {
|
||
|
arg = argv[i];
|
||
|
|
||
|
if (arg[0] == '-' || arg[0] == '/') {
|
||
|
switch(arg[1]) {
|
||
|
case '?':
|
||
|
PrintUsage();
|
||
|
goto error_exit;
|
||
|
|
||
|
case 'd':
|
||
|
doReverseLookup = FALSE;
|
||
|
break;
|
||
|
|
||
|
case 'r':
|
||
|
Reverse = TRUE;
|
||
|
break;
|
||
|
|
||
|
case 'h':
|
||
|
if (!param(&maximumHops, argv, argc, i, 1, 255)) {
|
||
|
goto error_exit;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'w':
|
||
|
if (!param(&timeout, argv, argc, i, 1, 0xffffffff)) {
|
||
|
goto error_exit;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 's':
|
||
|
if (!get_source(argv[++i], &sourceAddress)) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_9, argv[i]);
|
||
|
// printf("Bad IPv6 address %s.\n", argv[i]);
|
||
|
|
||
|
goto error_exit;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_10, argv[i]);
|
||
|
// printf("%s is not a valid command option.\n", argv[i]);
|
||
|
|
||
|
PrintUsage();
|
||
|
goto error_exit;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
} else {
|
||
|
foundAddress = TRUE;
|
||
|
if (!get_pingee(argv[i], doReverseLookup, &address, &hostname)) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_11, argv[i]);
|
||
|
// printf("Unable to resolve target system name %s.\n", argv[i]);
|
||
|
|
||
|
goto error_exit;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!foundAddress) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_12);
|
||
|
// printf("A target name or address must be specified.\n");
|
||
|
|
||
|
PrintUsage();
|
||
|
goto error_exit;
|
||
|
}
|
||
|
|
||
|
Handle = CreateFileW(WIN_IPV6_DEVICE_NAME,
|
||
|
0, // desired access
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL, // security attributes
|
||
|
OPEN_EXISTING,
|
||
|
0, // flags & attributes
|
||
|
NULL); // template file
|
||
|
if (Handle == INVALID_HANDLE_VALUE) {
|
||
|
status = GetLastError();
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_13, status);
|
||
|
// printf("Unable to contact IPv6 driver. Error code %u.\n", status);
|
||
|
|
||
|
goto error_exit;
|
||
|
}
|
||
|
|
||
|
if (sourceAddress.sin6_family == 0) {
|
||
|
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(AF_INET6, 0, 0);
|
||
|
if (s == INVALID_SOCKET) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_14, WSAGetLastError());
|
||
|
// printf("Unable to create IPv6 socket, error code %d.\n",
|
||
|
// WSAGetLastError());
|
||
|
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This ioctl will fail if tracert6 is run on a system
|
||
|
// without the proper support in AFD and the stack.
|
||
|
// In that case, sourceAddress will be unchanged.
|
||
|
//
|
||
|
(void) WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY,
|
||
|
&address, sizeof address,
|
||
|
&sourceAddress, sizeof sourceAddress,
|
||
|
&BytesReturned, NULL, NULL);
|
||
|
|
||
|
closesocket(s);
|
||
|
}
|
||
|
|
||
|
if (hostname)
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_15,
|
||
|
hostname, format_addr(&address));
|
||
|
// printf("\nTracing route to %s [%s]\n",
|
||
|
// hostname, format_addr(&address));
|
||
|
|
||
|
else
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_16, format_addr(&address));
|
||
|
// printf("\nTracing route to %s\n",
|
||
|
// format_addr(&address));
|
||
|
|
||
|
if (sourceAddress.sin6_family != 0)
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_17, format_addr(&sourceAddress));
|
||
|
// printf("from %s ", format_addr(&sourceAddress));
|
||
|
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_18, maximumHops);
|
||
|
// printf("over a maximum of %u hops:\n\n", maximumHops);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialize the echo request.
|
||
|
//
|
||
|
CopyTDIFromSA6(&request->DstAddress, &address);
|
||
|
CopyTDIFromSA6(&request->SrcAddress, &sourceAddress);
|
||
|
request->Flags = 0;
|
||
|
if (Reverse)
|
||
|
request->Flags |= ICMPV6_ECHO_REQUEST_FLAG_REVERSE;
|
||
|
request->Timeout = timeout;
|
||
|
memset(request + 1, 0, DEFAULT_SEND_SIZE);
|
||
|
|
||
|
while((Ttl <= maximumHops) && (Ttl != 0)) {
|
||
|
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_19, Ttl);
|
||
|
// printf("%3u ", Ttl);
|
||
|
|
||
|
|
||
|
haveReply = FALSE;
|
||
|
request->TTL = Ttl;
|
||
|
|
||
|
for (i=0; i<3; i++) {
|
||
|
|
||
|
if (! DeviceIoControl(Handle, IOCTL_ICMPV6_ECHO_REQUEST, request,
|
||
|
sizeof *request + DEFAULT_SEND_SIZE, reply,
|
||
|
sizeof *reply + DEFAULT_SEND_SIZE,
|
||
|
&bytesReturned, NULL)) {
|
||
|
|
||
|
status = GetLastError();
|
||
|
} else {
|
||
|
status = reply->Status;
|
||
|
}
|
||
|
|
||
|
if (status == IP_SUCCESS) {
|
||
|
print_time(reply->RoundTripTime);
|
||
|
|
||
|
haveReply = TRUE;
|
||
|
CopySAFromTDI6(&replyAddress, &reply->Address);
|
||
|
|
||
|
if (i == 2) {
|
||
|
print_ip_addr(&replyAddress, doReverseLookup);
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_20);
|
||
|
// printf("\n");
|
||
|
|
||
|
goto loop_end;
|
||
|
}
|
||
|
} else if (status == IP_HOP_LIMIT_EXCEEDED) {
|
||
|
print_time(reply->RoundTripTime);
|
||
|
|
||
|
haveReply = TRUE;
|
||
|
CopySAFromTDI6(&replyAddress, &reply->Address);
|
||
|
|
||
|
if (i == 2) {
|
||
|
print_ip_addr(&replyAddress, doReverseLookup);
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_20);
|
||
|
// printf("\n");
|
||
|
|
||
|
}
|
||
|
} else if (status == IP_REQ_TIMED_OUT) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_21);
|
||
|
// printf(" * ");
|
||
|
|
||
|
if (i == 2) {
|
||
|
if (haveReply) {
|
||
|
print_ip_addr(&replyAddress, doReverseLookup);
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_20);
|
||
|
// printf("\n");
|
||
|
|
||
|
} else {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_22);
|
||
|
// printf("Request timed out.\n");
|
||
|
|
||
|
}
|
||
|
}
|
||
|
} else if (status < IP_STATUS_BASE) {
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_23, status);
|
||
|
// printf("Transmit error: code %u\n", status);
|
||
|
|
||
|
goto loop_end;
|
||
|
} else {
|
||
|
//
|
||
|
// Fatal error.
|
||
|
//
|
||
|
if (reply != NULL) {
|
||
|
CopySAFromTDI6(&replyAddress, &reply->Address);
|
||
|
print_ip_addr(&replyAddress, doReverseLookup);
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_24);
|
||
|
// printf(" reports: ");
|
||
|
|
||
|
}
|
||
|
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_25, GetErrorString(status));
|
||
|
// printf("%s\n", GetErrorString(status));
|
||
|
|
||
|
goto loop_end;
|
||
|
}
|
||
|
|
||
|
if (reply->RoundTripTime < MIN_INTERVAL) {
|
||
|
Sleep(MIN_INTERVAL - reply->RoundTripTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ttl++;
|
||
|
}
|
||
|
|
||
|
loop_end:
|
||
|
|
||
|
NlsPutMsg(STDOUT, TRACERT6_MESSAGE_26);
|
||
|
// printf("\nTrace complete.\n");
|
||
|
|
||
|
return(0);
|
||
|
|
||
|
error_exit:
|
||
|
|
||
|
return(1);
|
||
|
}
|
||
|
|