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.

623 lines
18 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. // Packet INternet Groper utility for IPv6.
  14. //
  15. #include <windows.h>
  16. #include <devioctl.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <winsock2.h>
  20. #include <ws2tcpip.h>
  21. #include <wspiapi.h>
  22. // Need ntddip6 before ws2ip6 to get CopyTDIFromSA6 and CopySAFromTDI6.
  23. #include <ntddip6.h>
  24. #include <ws2ip6.h>
  25. //
  26. // Localization library and MessageIds.
  27. //
  28. #include <iphlpapi.h>
  29. #include <nls.h>
  30. #include "localmsg.h"
  31. #define MAX_BUFFER_SIZE (sizeof(ICMPV6_ECHO_REPLY) + 0xfff7)
  32. #define DEFAULT_BUFFER_SIZE (0x2000 - 8)
  33. #define DEFAULT_SEND_SIZE 32
  34. #define DEFAULT_COUNT 4
  35. #define DEFAULT_TIMEOUT 4000L
  36. #define MIN_INTERVAL 1000L
  37. struct sockaddr_in6 dstaddr, srcaddr;
  38. PWCHAR
  39. GetErrorString(int ErrorCode)
  40. {
  41. DWORD Status;
  42. DWORD Length;
  43. static WCHAR ErrorString[2048]; // a 2K static buffer should suffice
  44. Length = 2048;
  45. Status = GetIpErrorString(ErrorCode, ErrorString, &Length);
  46. if (Status == NO_ERROR) {
  47. return ErrorString; // success
  48. }
  49. return L""; // return a null string
  50. }
  51. void
  52. PrintUsage(void)
  53. {
  54. NlsPutMsg(STDOUT, PING6_MESSAGE_0);
  55. // printf("\nUsage: ping6 [-t] [-a] [-n count] [-l size]"
  56. // " [-w timeout] [-s srcaddr] dest\n\n"
  57. // "Options:\n"
  58. // "-t Ping the specifed host until interrupted.\n"
  59. // "-a Resolve addresses to hostnames.\n"
  60. // "-n count Number of echo requests to send.\n"
  61. // "-l size Send buffer size.\n"
  62. // "-w timeout Timeout in milliseconds to wait for each reply.\n"
  63. // "-s srcaddr Source address to use.\n"
  64. // "-r Use routing header to test reverse route also.\n");
  65. }
  66. //
  67. // Can only be called once, because
  68. // a) does not call freeaddrinfo
  69. // b) uses a static buffer for some results
  70. //
  71. int
  72. get_pingee(char *ahstr, int dnsreq, struct sockaddr_in6 *address, char **hstr)
  73. {
  74. struct addrinfo hints;
  75. struct addrinfo *result;
  76. char *name = NULL;
  77. memset(&hints, 0, sizeof hints);
  78. hints.ai_flags = AI_NUMERICHOST;
  79. hints.ai_family = PF_INET6;
  80. if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
  81. //
  82. // Not a numeric address.
  83. // Try again with DNS name resolution.
  84. //
  85. hints.ai_flags = AI_CANONNAME;
  86. if (getaddrinfo(ahstr, NULL, &hints, &result) != 0) {
  87. //
  88. // Failure - we can not resolve the name.
  89. //
  90. return FALSE;
  91. }
  92. name = result->ai_canonname;
  93. }
  94. else {
  95. //
  96. // Should we do a reverse-lookup to get a name?
  97. //
  98. if (dnsreq) {
  99. static char namebuf[NI_MAXHOST];
  100. if (getnameinfo(result->ai_addr, result->ai_addrlen,
  101. namebuf, sizeof namebuf,
  102. NULL, 0,
  103. NI_NAMEREQD) == 0) {
  104. //
  105. // Reverse lookup succeeded.
  106. //
  107. name = namebuf;
  108. }
  109. }
  110. }
  111. *address = * (struct sockaddr_in6 *) result->ai_addr;
  112. *hstr = name;
  113. return TRUE;
  114. }
  115. int
  116. get_source(char *astr, struct sockaddr_in6 *address)
  117. {
  118. struct addrinfo hints;
  119. struct addrinfo *result;
  120. memset(&hints, 0, sizeof hints);
  121. hints.ai_flags = AI_PASSIVE;
  122. hints.ai_family = PF_INET6;
  123. if (getaddrinfo(astr, NULL, &hints, &result) != 0)
  124. return FALSE;
  125. *address = * (struct sockaddr_in6 *) result->ai_addr;
  126. return TRUE;
  127. }
  128. char *
  129. format_addr(struct sockaddr_in6 *address)
  130. {
  131. static char buffer[128];
  132. if (getnameinfo((struct sockaddr *)address, sizeof *address,
  133. buffer, sizeof buffer, NULL, 0, NI_NUMERICHOST) != 0)
  134. strcpy(buffer, "<invalid>");
  135. return buffer;
  136. }
  137. u_long
  138. param(char **argv, int argc, int current, u_long min, u_long max)
  139. {
  140. u_long temp;
  141. char *dummy;
  142. if (current == (argc - 1) ) {
  143. NlsPutMsg(STDOUT, PING6_MESSAGE_1, argv[current]);
  144. // printf("Value must be supplied for option %s.\n", argv[current]);
  145. exit(1);
  146. }
  147. temp = strtoul(argv[current+1], &dummy, 0);
  148. if (temp < min || temp > max) {
  149. NlsPutMsg(STDOUT, PING6_MESSAGE_2, argv[current]);
  150. // printf("Bad value for option %s.\n", argv[current]);
  151. exit(1);
  152. }
  153. return temp;
  154. }
  155. u_int num_send=0, num_recv=0,
  156. time_min=(u_int)-1, time_max=0, time_total=0;
  157. void
  158. print_statistics( )
  159. {
  160. if (num_send > 0) {
  161. NlsPutMsg(STDOUT, PING6_MESSAGE_3, format_addr(&dstaddr));
  162. // printf("\nPing statistics for %s:\n", format_addr(&dstaddr));
  163. NlsPutMsg(STDOUT, PING6_MESSAGE_4,
  164. num_send, num_recv, num_send - num_recv,
  165. 100 * (num_send - num_recv) / num_send);
  166. // printf(" Packets: Sent = %u, Received = %u, Lost = %u (%u%% loss),\n",
  167. // num_send, num_recv, num_send - num_recv,
  168. // 100 * (num_send - num_recv) / num_send);
  169. if (num_recv > 0) {
  170. NlsPutMsg(STDOUT, PING6_MESSAGE_5);
  171. // printf("Approximate round trip times in milli-seconds:\n");
  172. NlsPutMsg(STDOUT, PING6_MESSAGE_6,
  173. time_min, time_max, time_total / num_recv);
  174. // printf(" Minimum = %ums, Maximum = %ums, Average = %ums\n",
  175. // time_min, time_max, time_total / num_recv);
  176. }
  177. }
  178. }
  179. // Press C-c to print and abort.
  180. // Press C-break to print and continue.
  181. BOOL
  182. ConsoleControlHandler(DWORD dwCtrlType)
  183. {
  184. print_statistics();
  185. switch ( dwCtrlType ) {
  186. case CTRL_BREAK_EVENT:
  187. NlsPutMsg(STDOUT, PING6_MESSAGE_7);
  188. // printf("Control-Break\n");
  189. return TRUE;
  190. case CTRL_C_EVENT:
  191. NlsPutMsg(STDOUT, PING6_MESSAGE_8);
  192. // printf("Control-C\n");
  193. default:
  194. return FALSE;
  195. }
  196. }
  197. int __cdecl
  198. main(int argc, char **argv)
  199. {
  200. char *arg;
  201. u_int i;
  202. u_int j;
  203. int found_addr = FALSE;
  204. int dnsreq = FALSE;
  205. char *hostname = NULL;
  206. u_int Count = DEFAULT_COUNT;
  207. u_long Timeout = DEFAULT_TIMEOUT;
  208. DWORD errorCode;
  209. HANDLE Handle;
  210. int err;
  211. BOOL result;
  212. PICMPV6_ECHO_REQUEST request;
  213. PICMPV6_ECHO_REPLY reply;
  214. char *SendBuffer, *RcvBuffer;
  215. u_int RcvSize;
  216. u_int SendSize = DEFAULT_SEND_SIZE;
  217. u_int ReplySize;
  218. DWORD bytesReturned;
  219. WSADATA WsaData;
  220. int Reverse = FALSE;
  221. PWCHAR Message;
  222. memset(&srcaddr, 0, sizeof srcaddr);
  223. memset(&dstaddr, 0, sizeof dstaddr);
  224. err = WSAStartup(MAKEWORD(2, 0), &WsaData);
  225. if (err) {
  226. NlsPutMsg(STDOUT, PING6_MESSAGE_9, GetLastError());
  227. // printf("Unable to initialize Windows Sockets interface, error code %d\n", GetLastError());
  228. exit(1);
  229. }
  230. if (argc < 2) {
  231. PrintUsage();
  232. exit(1);
  233. } else {
  234. i = 1;
  235. while (i < (u_int) argc) {
  236. arg = argv[i];
  237. if (arg[0] == '-' || arg[0] == '/') { // Have an option
  238. switch (arg[1]) {
  239. case '?':
  240. PrintUsage();
  241. exit(0);
  242. case 'l':
  243. // Avoid jumbo-grams, we don't support them yet.
  244. // Need to allow 8 bytes for the Echo Request header.
  245. SendSize = (u_int)param(argv, argc, i++, 0, 0xffff - 8);
  246. break;
  247. case 't':
  248. Count = (u_int)-1;
  249. break;
  250. case 'n':
  251. Count = (u_int)param(argv, argc, i++, 1, 0xffffffff);
  252. break;
  253. case 'w':
  254. Timeout = param(argv, argc, i++, 0, 0xffffffff);
  255. break;
  256. case 'a':
  257. dnsreq = TRUE;
  258. break;
  259. case 'r':
  260. Reverse = TRUE;
  261. break;
  262. case 's':
  263. if (!get_source(argv[++i], &srcaddr)) {
  264. NlsPutMsg(STDOUT, PING6_MESSAGE_10, argv[i]);
  265. // printf("Bad IPv6 address %s.\n", argv[i]);
  266. exit(1);
  267. }
  268. break;
  269. default:
  270. NlsPutMsg(STDOUT, PING6_MESSAGE_11, arg);
  271. // printf("Bad option %s.\n\n", arg);
  272. PrintUsage();
  273. exit(1);
  274. break;
  275. }
  276. i++;
  277. } else { // Not an option, must be an IPv6 address.
  278. if (found_addr) {
  279. NlsPutMsg(STDOUT, PING6_MESSAGE_12, arg);
  280. // printf("Bad parameter %s.\n", arg);
  281. exit(1);
  282. }
  283. if (get_pingee(arg, dnsreq, &dstaddr, &hostname)) {
  284. found_addr = TRUE;
  285. i++;
  286. } else {
  287. NlsPutMsg(STDOUT, PING6_MESSAGE_10, arg);
  288. // printf("Bad IPv6 address %s.\n", arg);
  289. exit(1);
  290. }
  291. }
  292. }
  293. }
  294. if (!found_addr) {
  295. NlsPutMsg(STDOUT, PING6_MESSAGE_13);
  296. // printf("IPv6 address must be specified.\n");
  297. exit(1);
  298. }
  299. Handle = CreateFileW(WIN_IPV6_DEVICE_NAME,
  300. 0, // desired access
  301. FILE_SHARE_READ | FILE_SHARE_WRITE,
  302. NULL, // security attributes
  303. OPEN_EXISTING,
  304. 0, // flags & attributes
  305. NULL); // template file
  306. if (Handle == INVALID_HANDLE_VALUE) {
  307. NlsPutMsg(STDOUT, PING6_MESSAGE_14,
  308. GetLastError() );
  309. // printf("Unable to contact IPv6 driver, error code %d.\n",
  310. // GetLastError() );
  311. exit(1);
  312. }
  313. if (srcaddr.sin6_family == 0) {
  314. SOCKET s;
  315. DWORD BytesReturned;
  316. //
  317. // A source address was not specified.
  318. // Get the preferred source address for this destination.
  319. //
  320. // If you want each individual echo request
  321. // to select a source address, use "-s ::".
  322. //
  323. s = socket(AF_INET6, 0, 0);
  324. if (s == INVALID_SOCKET) {
  325. NlsPutMsg(STDOUT, PING6_MESSAGE_15,
  326. WSAGetLastError());
  327. // printf("Unable to create IPv6 socket, error code %d.\n",
  328. // WSAGetLastError());
  329. exit(1);
  330. }
  331. //
  332. // This ioctl will fail if ping6 is run on a system
  333. // without the proper support in AFD and the stack.
  334. // In that case, srcaddr will be unchanged.
  335. //
  336. (void) WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY,
  337. &dstaddr, sizeof dstaddr,
  338. &srcaddr, sizeof srcaddr,
  339. &BytesReturned, NULL, NULL);
  340. closesocket(s);
  341. }
  342. request = LocalAlloc(LMEM_FIXED, sizeof *request + SendSize);
  343. if (request == NULL) {
  344. NlsPutMsg(STDOUT, PING6_MESSAGE_16);
  345. // printf("Unable to allocate required memory.\n");
  346. exit(1);
  347. }
  348. SendBuffer = (char *)(request + 1);
  349. //
  350. // Calculate receive buffer size and try to allocate it.
  351. //
  352. if (SendSize <= DEFAULT_SEND_SIZE) {
  353. RcvSize = DEFAULT_BUFFER_SIZE;
  354. }
  355. else {
  356. RcvSize = MAX_BUFFER_SIZE;
  357. }
  358. reply = LocalAlloc(LMEM_FIXED, sizeof *reply + RcvSize);
  359. if (reply == NULL) {
  360. NlsPutMsg(STDOUT, PING6_MESSAGE_16);
  361. // printf("Unable to allocate required memory.\n");
  362. exit(1);
  363. }
  364. RcvBuffer = (char *)(reply + 1);
  365. //
  366. // Initialize the request buffer.
  367. //
  368. CopyTDIFromSA6(&request->DstAddress, &dstaddr);
  369. CopyTDIFromSA6(&request->SrcAddress, &srcaddr);
  370. request->Flags = 0;
  371. if (Reverse)
  372. request->Flags |= ICMPV6_ECHO_REQUEST_FLAG_REVERSE;
  373. request->Timeout = Timeout;
  374. request->TTL = 0; // default TTL
  375. //
  376. // Initialize the request data pattern.
  377. //
  378. for (i = 0; i < SendSize; i++)
  379. SendBuffer[i] = 'a' + (i % 23);
  380. if (hostname)
  381. NlsPutMsg(STDOUT, PING6_MESSAGE_17, hostname, format_addr(&dstaddr));
  382. // printf("\nPinging %s [%s]", hostname, format_addr(&dstaddr));
  383. else
  384. NlsPutMsg(STDOUT, PING6_MESSAGE_18, format_addr(&dstaddr));
  385. // printf("\nPinging %s", format_addr(&dstaddr));
  386. if (srcaddr.sin6_family != 0)
  387. NlsPutMsg(STDOUT, PING6_MESSAGE_19, format_addr(&srcaddr));
  388. // printf("\nfrom %s", format_addr(&srcaddr));
  389. NlsPutMsg(STDOUT, PING6_MESSAGE_20, SendSize);
  390. // printf(" with %u bytes of data:\n\n", SendSize);
  391. SetConsoleCtrlHandler(&ConsoleControlHandler, TRUE);
  392. for (i = 0; i < Count; i++) {
  393. if (! DeviceIoControl(Handle,
  394. IOCTL_ICMPV6_ECHO_REQUEST,
  395. request,
  396. sizeof *request + SendSize,
  397. reply, sizeof *reply + RcvSize,
  398. &bytesReturned,
  399. NULL) ||
  400. (bytesReturned < sizeof *reply)) {
  401. errorCode = GetLastError();
  402. if (errorCode < IP_STATUS_BASE) {
  403. NlsPutMsg(STDOUT, PING6_MESSAGE_21, errorCode);
  404. // printf("PING: transmit failed, error code %u\n", errorCode);
  405. }
  406. else {
  407. NlsPutMsg(STDOUT, PING6_MESSAGE_22,
  408. GetErrorString(errorCode));
  409. // printf("PING: %s\n", GetErrorString(errorCode));
  410. }
  411. }
  412. else {
  413. struct sockaddr_in6 ReplyAddress;
  414. ReplySize = bytesReturned - sizeof *reply;
  415. CopySAFromTDI6(&ReplyAddress, &reply->Address);
  416. num_send++;
  417. if (! IN6_IS_ADDR_UNSPECIFIED(&ReplyAddress.sin6_addr))
  418. NlsPutMsg(STDOUT, PING6_MESSAGE_23, format_addr(&ReplyAddress));
  419. // printf("Reply from %s: ", format_addr(&ReplyAddress));
  420. if (reply->Status == IP_SUCCESS) {
  421. NlsPutMsg(STDOUT, PING6_MESSAGE_24, ReplySize);
  422. // printf("bytes=%u ", ReplySize);
  423. if (ReplySize != SendSize) {
  424. NlsPutMsg(STDOUT, PING6_MESSAGE_25, SendSize);
  425. // printf("(sent %u) ", SendSize);
  426. }
  427. else {
  428. char *sendptr, *recvptr;
  429. sendptr = SendBuffer;
  430. recvptr = RcvBuffer;
  431. for (j = 0; j < SendSize; j++)
  432. if (*sendptr++ != *recvptr++) {
  433. NlsPutMsg(STDOUT, PING6_MESSAGE_26, j);
  434. // printf("- MISCOMPARE at offset %u - ", j);
  435. break;
  436. }
  437. }
  438. if (reply->RoundTripTime) {
  439. NlsPutMsg(STDOUT, PING6_MESSAGE_27, reply->RoundTripTime);
  440. // printf("time=%ums ", reply->RoundTripTime);
  441. }
  442. else {
  443. NlsPutMsg(STDOUT, PING6_MESSAGE_28);
  444. // printf("time<1ms ");
  445. }
  446. NlsPutMsg(STDOUT, PING6_MESSAGE_29);
  447. // printf("\n");
  448. //
  449. // Collect statistics.
  450. //
  451. num_recv++;
  452. time_total += reply->RoundTripTime;
  453. if (reply->RoundTripTime < time_min)
  454. time_min = reply->RoundTripTime;
  455. if (reply->RoundTripTime > time_max)
  456. time_max = reply->RoundTripTime;
  457. }
  458. else {
  459. NlsPutMsg(STDOUT, PING6_MESSAGE_30,
  460. GetErrorString(reply->Status));
  461. // printf("%s\n", GetErrorString(reply->Status));
  462. if (IN6_IS_ADDR_UNSPECIFIED(&ReplyAddress.sin6_addr)) {
  463. //
  464. // Diagnose some common errors.
  465. //
  466. if (reply->Status == IP_DEST_NO_ROUTE) {
  467. if (IN6_IS_ADDR_LINKLOCAL(&dstaddr.sin6_addr) ||
  468. IN6_IS_ADDR_SITELOCAL(&dstaddr.sin6_addr) ||
  469. IN6_IS_ADDR_MULTICAST(&dstaddr.sin6_addr))
  470. NlsPutMsg(STDOUT, PING6_MESSAGE_31);
  471. // printf(" Specify correct scope-id or "
  472. // "use -s to specify source address.\n");
  473. }
  474. else if (reply->Status == IP_PARAMETER_PROBLEM) {
  475. if (IN6_IS_ADDR_UNSPECIFIED(&dstaddr.sin6_addr))
  476. NlsPutMsg(STDOUT, PING6_MESSAGE_32);
  477. // printf(" Illegal destination address.\n");
  478. else if (dstaddr.sin6_scope_id != 0)
  479. NlsPutMsg(STDOUT, PING6_MESSAGE_33);
  480. // printf(" Invalid scope-id specified.\n");
  481. else if (IN6_IS_ADDR_LINKLOCAL(&dstaddr.sin6_addr) ||
  482. IN6_IS_ADDR_SITELOCAL(&dstaddr.sin6_addr) ||
  483. IN6_IS_ADDR_MULTICAST(&dstaddr.sin6_addr))
  484. NlsPutMsg(STDOUT, PING6_MESSAGE_31);
  485. // printf(" Specify correct scope-id or "
  486. // "use -s to specify source address.\n");
  487. }
  488. else if (reply->Status == IP_BAD_ROUTE) {
  489. NlsPutMsg(STDOUT, PING6_MESSAGE_34);
  490. // printf(" Problem with source address or scope-id.\n");
  491. }
  492. }
  493. }
  494. if (i < (Count - 1)) {
  495. if (reply->RoundTripTime < MIN_INTERVAL) {
  496. Sleep(MIN_INTERVAL - reply->RoundTripTime);
  497. }
  498. }
  499. }
  500. }
  501. print_statistics();
  502. return num_recv == 0;
  503. }