// -*- 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 #include #include #include #include #include #include #include // Need ntddip6 before ws2ip6 to get CopyTDIFromSA6 and CopySAFromTDI6. #include #include // // Localization library and MessageIds. // #include #include #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, ""); 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, ""); 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); }