Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

534 lines
14 KiB

  1. // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
  2. //
  3. // Copyright (c) 1985-2000 Microsoft Corporation
  4. //
  5. // This file is part of the Microsoft Research IPv6 Network Protocol Stack.
  6. // You should have received a copy of the Microsoft End-User License Agreement
  7. // for this software along with this release; see the file "license.txt".
  8. // If not, please see http://www.research.microsoft.com/msripv6/license.htm,
  9. // or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
  10. //
  11. // Abstract:
  12. //
  13. // TraceRoute utility for IPv6.
  14. //
  15. #include <windows.h>
  16. #include <devioctl.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <malloc.h>
  20. #include <winsock2.h>
  21. #include <ws2tcpip.h>
  22. #include <wspiapi.h>
  23. // Need ntddip6 before ws2ip6 to get CopyTDIFromSA6 and CopySAFromTDI6.
  24. #include <ntddip6.h>
  25. #include <ws2ip6.h>
  26. //
  27. // Localization library and MessageIds.
  28. //
  29. #include <iphlpapi.h>
  30. #include <nls.h>
  31. #include "localmsg.h"
  32. #define DEFAULT_MAXIMUM_HOPS 30
  33. #define DEFAULT_SEND_SIZE 64
  34. #define DEFAULT_TIMEOUT 3000L
  35. #define MIN_INTERVAL 1000L
  36. PWCHAR
  37. GetErrorString(int ErrorCode)
  38. {
  39. DWORD Status;
  40. DWORD Length;
  41. static WCHAR ErrorString[2048]; // a 2K static buffer should suffice
  42. Length = 2048;
  43. Status = GetIpErrorString(ErrorCode, ErrorString, &Length);
  44. if (Status == NO_ERROR) {
  45. return ErrorString; // success
  46. }
  47. return L""; // return a null string
  48. }
  49. char *
  50. format_addr(struct sockaddr_in6 *address)
  51. {
  52. static char buffer[128];
  53. if (getnameinfo((struct sockaddr *)address, sizeof *address,
  54. buffer, sizeof buffer, NULL, 0, NI_NUMERICHOST) != 0)
  55. strcpy(buffer, "<invalid>");
  56. return buffer;
  57. }
  58. void
  59. print_ip_addr(struct sockaddr_in6 *addr, BOOLEAN DoReverseLookup)
  60. {
  61. char stringBuffer[128];
  62. BOOLEAN didReverse = FALSE;
  63. if (DoReverseLookup) {
  64. if (getnameinfo((struct sockaddr *)addr, sizeof *addr,
  65. stringBuffer, sizeof stringBuffer,
  66. NULL, 0, NI_NAMEREQD) == 0) {
  67. didReverse = TRUE;
  68. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_0, stringBuffer);
  69. // printf("%s ", stringBuffer);
  70. }
  71. }
  72. if (getnameinfo((struct sockaddr *)addr, sizeof *addr,
  73. stringBuffer, sizeof stringBuffer,
  74. NULL, 0, NI_NUMERICHOST) != 0)
  75. strcpy(stringBuffer, "<invalid>");
  76. if (didReverse) {
  77. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_1, stringBuffer);
  78. // printf("[%s]", stringBuffer);
  79. } else {
  80. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_2, stringBuffer);
  81. // printf("%s", stringBuffer);
  82. }
  83. }
  84. void
  85. print_time(u_long Time)
  86. {
  87. if (Time) {
  88. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_3, Time);
  89. // printf("%4u ms ", Time);
  90. } else {
  91. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_4);
  92. // printf(" <1 ms ");
  93. }
  94. }
  95. BOOLEAN
  96. param(
  97. u_long *parameter,
  98. char **argv,
  99. int argc,
  100. int current,
  101. u_long min,
  102. u_long max)
  103. {
  104. u_long temp;
  105. char *dummy;
  106. if (current == (argc - 1)) {
  107. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_5, argv[current]);
  108. // printf("A value must be supplied for option %s.\n", argv[current]);
  109. return(FALSE);
  110. }
  111. temp = strtoul(argv[current+1], &dummy, 0);
  112. if (temp < min || temp > max) {
  113. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_6, argv[current]);
  114. // printf("Bad value for option %s.\n", argv[current]);
  115. return(FALSE);
  116. }
  117. *parameter = temp;
  118. return(TRUE);
  119. }
  120. //
  121. // Can only be called once, because
  122. // a) does not call freeaddrinfo
  123. // b) uses a static buffer for some results
  124. //
  125. int
  126. get_pingee(char *ahstr, int dnsreq, struct sockaddr_in6 *address, char **hstr)
  127. {
  128. struct addrinfo hints;
  129. struct addrinfo *result;
  130. char *name = NULL;
  131. memset(&hints, 0, sizeof hints);
  132. hints.ai_flags = AI_NUMERICHOST;
  133. hints.ai_family = PF_INET6;
  134. if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
  135. //
  136. // Not a numeric address.
  137. // Try again with DNS name resolution.
  138. //
  139. hints.ai_flags = AI_CANONNAME;
  140. if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
  141. //
  142. // Failure - we can not resolve the name.
  143. //
  144. return FALSE;
  145. }
  146. name = result->ai_canonname;
  147. }
  148. else {
  149. //
  150. // Should we do a reverse-lookup to get a name?
  151. //
  152. if (dnsreq) {
  153. static char namebuf[NI_MAXHOST];
  154. if (getnameinfo(result->ai_addr, result->ai_addrlen,
  155. namebuf, sizeof namebuf,
  156. NULL, 0,
  157. NI_NAMEREQD) == 0) {
  158. //
  159. // Reverse lookup succeeded.
  160. //
  161. name = namebuf;
  162. }
  163. }
  164. }
  165. *address = * (struct sockaddr_in6 *) result->ai_addr;
  166. *hstr = name;
  167. return TRUE;
  168. }
  169. int
  170. get_source(char *astr, struct sockaddr_in6 *address)
  171. {
  172. struct addrinfo hints;
  173. struct addrinfo *result;
  174. memset(&hints, 0, sizeof hints);
  175. hints.ai_flags = AI_PASSIVE;
  176. hints.ai_family = PF_INET6;
  177. if (getaddrinfo(astr, NULL, &hints, &result) != 0)
  178. return FALSE;
  179. *address = * (struct sockaddr_in6 *) result->ai_addr;
  180. return TRUE;
  181. }
  182. void
  183. PrintUsage(void)
  184. {
  185. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_7);
  186. // printf("\nUsage: tracert6 [-d] [-h maximum_hops] [-w timeout] "
  187. // "[-s srcaddr] target_name\n\n"
  188. // "Options:\n"
  189. // "-d Do not resolve addresses to hostnames.\n"
  190. // "-h max_hops Maximum number of hops to search for target.\n"
  191. // "-w timeout Wait timeout milliseconds for each reply.\n"
  192. // "-s srcaddr Source address to use.\n"
  193. // "-r Use routing header to test reverse route also.\n");
  194. }
  195. int __cdecl
  196. main(int argc, char **argv)
  197. {
  198. struct sockaddr_in6 address, sourceAddress, replyAddress;
  199. u_short RcvSize;
  200. BOOL result;
  201. DWORD bytesReturned;
  202. HANDLE Handle;
  203. DWORD status;
  204. PICMPV6_ECHO_REQUEST request;
  205. PICMPV6_ECHO_REPLY reply;
  206. char *hostname;
  207. char *arg;
  208. int i;
  209. u_long maximumHops = DEFAULT_MAXIMUM_HOPS;
  210. BOOLEAN doReverseLookup = TRUE;
  211. u_long timeout = DEFAULT_TIMEOUT;
  212. BOOLEAN foundAddress = FALSE;
  213. BOOLEAN haveReply;
  214. u_char Ttl = 1;
  215. WSADATA WsaData;
  216. int Reverse = FALSE;
  217. PWCHAR Message;
  218. memset(&address, 0, sizeof address);
  219. memset(&sourceAddress, 0, sizeof sourceAddress);
  220. request = alloca(sizeof *request + DEFAULT_SEND_SIZE);
  221. reply = alloca(sizeof *reply + DEFAULT_SEND_SIZE);
  222. if (WSAStartup( MAKEWORD(2, 0), &WsaData)) {
  223. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_8, GetLastError());
  224. // printf("Unable to initialize the Windows Sockets interface, "
  225. // "error code %u.\n", GetLastError());
  226. return(1);
  227. }
  228. if (argc < 2) {
  229. PrintUsage();
  230. goto error_exit;
  231. }
  232. //
  233. // process command line
  234. //
  235. for (i=1; i < argc; i++) {
  236. arg = argv[i];
  237. if (arg[0] == '-' || arg[0] == '/') {
  238. switch(arg[1]) {
  239. case '?':
  240. PrintUsage();
  241. goto error_exit;
  242. case 'd':
  243. doReverseLookup = FALSE;
  244. break;
  245. case 'r':
  246. Reverse = TRUE;
  247. break;
  248. case 'h':
  249. if (!param(&maximumHops, argv, argc, i, 1, 255)) {
  250. goto error_exit;
  251. }
  252. break;
  253. case 'w':
  254. if (!param(&timeout, argv, argc, i, 1, 0xffffffff)) {
  255. goto error_exit;
  256. }
  257. break;
  258. case 's':
  259. if (!get_source(argv[++i], &sourceAddress)) {
  260. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_9, argv[i]);
  261. // printf("Bad IPv6 address %s.\n", argv[i]);
  262. goto error_exit;
  263. }
  264. break;
  265. default:
  266. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_10, argv[i]);
  267. // printf("%s is not a valid command option.\n", argv[i]);
  268. PrintUsage();
  269. goto error_exit;
  270. break;
  271. }
  272. } else {
  273. foundAddress = TRUE;
  274. if (!get_pingee(argv[i], doReverseLookup, &address, &hostname)) {
  275. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_11, argv[i]);
  276. // printf("Unable to resolve target system name %s.\n", argv[i]);
  277. goto error_exit;
  278. }
  279. }
  280. }
  281. if (!foundAddress) {
  282. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_12);
  283. // printf("A target name or address must be specified.\n");
  284. PrintUsage();
  285. goto error_exit;
  286. }
  287. Handle = CreateFileW(WIN_IPV6_DEVICE_NAME,
  288. 0, // desired access
  289. FILE_SHARE_READ | FILE_SHARE_WRITE,
  290. NULL, // security attributes
  291. OPEN_EXISTING,
  292. 0, // flags & attributes
  293. NULL); // template file
  294. if (Handle == INVALID_HANDLE_VALUE) {
  295. status = GetLastError();
  296. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_13, status);
  297. // printf("Unable to contact IPv6 driver. Error code %u.\n", status);
  298. goto error_exit;
  299. }
  300. if (sourceAddress.sin6_family == 0) {
  301. SOCKET s;
  302. DWORD BytesReturned;
  303. //
  304. // A source address was not specified.
  305. // Get the preferred source address for this destination.
  306. //
  307. // If you want each individual echo request
  308. // to select a source address, use "-s ::".
  309. //
  310. s = socket(AF_INET6, 0, 0);
  311. if (s == INVALID_SOCKET) {
  312. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_14, WSAGetLastError());
  313. // printf("Unable to create IPv6 socket, error code %d.\n",
  314. // WSAGetLastError());
  315. exit(1);
  316. }
  317. //
  318. // This ioctl will fail if tracert6 is run on a system
  319. // without the proper support in AFD and the stack.
  320. // In that case, sourceAddress will be unchanged.
  321. //
  322. (void) WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY,
  323. &address, sizeof address,
  324. &sourceAddress, sizeof sourceAddress,
  325. &BytesReturned, NULL, NULL);
  326. closesocket(s);
  327. }
  328. if (hostname)
  329. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_15,
  330. hostname, format_addr(&address));
  331. // printf("\nTracing route to %s [%s]\n",
  332. // hostname, format_addr(&address));
  333. else
  334. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_16, format_addr(&address));
  335. // printf("\nTracing route to %s\n",
  336. // format_addr(&address));
  337. if (sourceAddress.sin6_family != 0)
  338. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_17, format_addr(&sourceAddress));
  339. // printf("from %s ", format_addr(&sourceAddress));
  340. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_18, maximumHops);
  341. // printf("over a maximum of %u hops:\n\n", maximumHops);
  342. //
  343. // Initialize the echo request.
  344. //
  345. CopyTDIFromSA6(&request->DstAddress, &address);
  346. CopyTDIFromSA6(&request->SrcAddress, &sourceAddress);
  347. request->Flags = 0;
  348. if (Reverse)
  349. request->Flags |= ICMPV6_ECHO_REQUEST_FLAG_REVERSE;
  350. request->Timeout = timeout;
  351. memset(request + 1, 0, DEFAULT_SEND_SIZE);
  352. while((Ttl <= maximumHops) && (Ttl != 0)) {
  353. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_19, Ttl);
  354. // printf("%3u ", Ttl);
  355. haveReply = FALSE;
  356. request->TTL = Ttl;
  357. for (i=0; i<3; i++) {
  358. if (! DeviceIoControl(Handle, IOCTL_ICMPV6_ECHO_REQUEST, request,
  359. sizeof *request + DEFAULT_SEND_SIZE, reply,
  360. sizeof *reply + DEFAULT_SEND_SIZE,
  361. &bytesReturned, NULL)) {
  362. status = GetLastError();
  363. } else {
  364. status = reply->Status;
  365. }
  366. if (status == IP_SUCCESS) {
  367. print_time(reply->RoundTripTime);
  368. haveReply = TRUE;
  369. CopySAFromTDI6(&replyAddress, &reply->Address);
  370. if (i == 2) {
  371. print_ip_addr(&replyAddress, doReverseLookup);
  372. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_20);
  373. // printf("\n");
  374. goto loop_end;
  375. }
  376. } else if (status == IP_HOP_LIMIT_EXCEEDED) {
  377. print_time(reply->RoundTripTime);
  378. haveReply = TRUE;
  379. CopySAFromTDI6(&replyAddress, &reply->Address);
  380. if (i == 2) {
  381. print_ip_addr(&replyAddress, doReverseLookup);
  382. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_20);
  383. // printf("\n");
  384. }
  385. } else if (status == IP_REQ_TIMED_OUT) {
  386. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_21);
  387. // printf(" * ");
  388. if (i == 2) {
  389. if (haveReply) {
  390. print_ip_addr(&replyAddress, doReverseLookup);
  391. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_20);
  392. // printf("\n");
  393. } else {
  394. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_22);
  395. // printf("Request timed out.\n");
  396. }
  397. }
  398. } else if (status < IP_STATUS_BASE) {
  399. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_23, status);
  400. // printf("Transmit error: code %u\n", status);
  401. goto loop_end;
  402. } else {
  403. //
  404. // Fatal error.
  405. //
  406. if (reply != NULL) {
  407. CopySAFromTDI6(&replyAddress, &reply->Address);
  408. print_ip_addr(&replyAddress, doReverseLookup);
  409. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_24);
  410. // printf(" reports: ");
  411. }
  412. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_25, GetErrorString(status));
  413. // printf("%s\n", GetErrorString(status));
  414. goto loop_end;
  415. }
  416. if (reply->RoundTripTime < MIN_INTERVAL) {
  417. Sleep(MIN_INTERVAL - reply->RoundTripTime);
  418. }
  419. }
  420. Ttl++;
  421. }
  422. loop_end:
  423. NlsPutMsg(STDOUT, TRACERT6_MESSAGE_26);
  424. // printf("\nTrace complete.\n");
  425. return(0);
  426. error_exit:
  427. return(1);
  428. }