windows-nt/Source/XPSP1/NT/net/tcpip/tpipv6/ping6/ping6.c
2020-09-26 16:20:57 +08:00

624 lines
18 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:
//
// Packet INternet Groper utility for IPv6.
//
#include <windows.h>
#include <devioctl.h>
#include <stdio.h>
#include <stdlib.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 MAX_BUFFER_SIZE (sizeof(ICMPV6_ECHO_REPLY) + 0xfff7)
#define DEFAULT_BUFFER_SIZE (0x2000 - 8)
#define DEFAULT_SEND_SIZE 32
#define DEFAULT_COUNT 4
#define DEFAULT_TIMEOUT 4000L
#define MIN_INTERVAL 1000L
struct sockaddr_in6 dstaddr, srcaddr;
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
}
void
PrintUsage(void)
{
NlsPutMsg(STDOUT, PING6_MESSAGE_0);
// printf("\nUsage: ping6 [-t] [-a] [-n count] [-l size]"
// " [-w timeout] [-s srcaddr] dest\n\n"
// "Options:\n"
// "-t Ping the specifed host until interrupted.\n"
// "-a Resolve addresses to hostnames.\n"
// "-n count Number of echo requests to send.\n"
// "-l size Send buffer size.\n"
// "-w timeout Timeout in milliseconds to wait for each reply.\n"
// "-s srcaddr Source address to use.\n"
// "-r Use routing header to test reverse route also.\n");
}
//
// 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;
}
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;
}
u_long
param(char **argv, int argc, int current, u_long min, u_long max)
{
u_long temp;
char *dummy;
if (current == (argc - 1) ) {
NlsPutMsg(STDOUT, PING6_MESSAGE_1, argv[current]);
// printf("Value must be supplied for option %s.\n", argv[current]);
exit(1);
}
temp = strtoul(argv[current+1], &dummy, 0);
if (temp < min || temp > max) {
NlsPutMsg(STDOUT, PING6_MESSAGE_2, argv[current]);
// printf("Bad value for option %s.\n", argv[current]);
exit(1);
}
return temp;
}
u_int num_send=0, num_recv=0,
time_min=(u_int)-1, time_max=0, time_total=0;
void
print_statistics( )
{
if (num_send > 0) {
NlsPutMsg(STDOUT, PING6_MESSAGE_3, format_addr(&dstaddr));
// printf("\nPing statistics for %s:\n", format_addr(&dstaddr));
NlsPutMsg(STDOUT, PING6_MESSAGE_4,
num_send, num_recv, num_send - num_recv,
100 * (num_send - num_recv) / num_send);
// printf(" Packets: Sent = %u, Received = %u, Lost = %u (%u%% loss),\n",
// num_send, num_recv, num_send - num_recv,
// 100 * (num_send - num_recv) / num_send);
if (num_recv > 0) {
NlsPutMsg(STDOUT, PING6_MESSAGE_5);
// printf("Approximate round trip times in milli-seconds:\n");
NlsPutMsg(STDOUT, PING6_MESSAGE_6,
time_min, time_max, time_total / num_recv);
// printf(" Minimum = %ums, Maximum = %ums, Average = %ums\n",
// time_min, time_max, time_total / num_recv);
}
}
}
// Press C-c to print and abort.
// Press C-break to print and continue.
BOOL
ConsoleControlHandler(DWORD dwCtrlType)
{
print_statistics();
switch ( dwCtrlType ) {
case CTRL_BREAK_EVENT:
NlsPutMsg(STDOUT, PING6_MESSAGE_7);
// printf("Control-Break\n");
return TRUE;
case CTRL_C_EVENT:
NlsPutMsg(STDOUT, PING6_MESSAGE_8);
// printf("Control-C\n");
default:
return FALSE;
}
}
int __cdecl
main(int argc, char **argv)
{
char *arg;
u_int i;
u_int j;
int found_addr = FALSE;
int dnsreq = FALSE;
char *hostname = NULL;
u_int Count = DEFAULT_COUNT;
u_long Timeout = DEFAULT_TIMEOUT;
DWORD errorCode;
HANDLE Handle;
int err;
BOOL result;
PICMPV6_ECHO_REQUEST request;
PICMPV6_ECHO_REPLY reply;
char *SendBuffer, *RcvBuffer;
u_int RcvSize;
u_int SendSize = DEFAULT_SEND_SIZE;
u_int ReplySize;
DWORD bytesReturned;
WSADATA WsaData;
int Reverse = FALSE;
PWCHAR Message;
memset(&srcaddr, 0, sizeof srcaddr);
memset(&dstaddr, 0, sizeof dstaddr);
err = WSAStartup(MAKEWORD(2, 0), &WsaData);
if (err) {
NlsPutMsg(STDOUT, PING6_MESSAGE_9, GetLastError());
// printf("Unable to initialize Windows Sockets interface, error code %d\n", GetLastError());
exit(1);
}
if (argc < 2) {
PrintUsage();
exit(1);
} else {
i = 1;
while (i < (u_int) argc) {
arg = argv[i];
if (arg[0] == '-' || arg[0] == '/') { // Have an option
switch (arg[1]) {
case '?':
PrintUsage();
exit(0);
case 'l':
// Avoid jumbo-grams, we don't support them yet.
// Need to allow 8 bytes for the Echo Request header.
SendSize = (u_int)param(argv, argc, i++, 0, 0xffff - 8);
break;
case 't':
Count = (u_int)-1;
break;
case 'n':
Count = (u_int)param(argv, argc, i++, 1, 0xffffffff);
break;
case 'w':
Timeout = param(argv, argc, i++, 0, 0xffffffff);
break;
case 'a':
dnsreq = TRUE;
break;
case 'r':
Reverse = TRUE;
break;
case 's':
if (!get_source(argv[++i], &srcaddr)) {
NlsPutMsg(STDOUT, PING6_MESSAGE_10, argv[i]);
// printf("Bad IPv6 address %s.\n", argv[i]);
exit(1);
}
break;
default:
NlsPutMsg(STDOUT, PING6_MESSAGE_11, arg);
// printf("Bad option %s.\n\n", arg);
PrintUsage();
exit(1);
break;
}
i++;
} else { // Not an option, must be an IPv6 address.
if (found_addr) {
NlsPutMsg(STDOUT, PING6_MESSAGE_12, arg);
// printf("Bad parameter %s.\n", arg);
exit(1);
}
if (get_pingee(arg, dnsreq, &dstaddr, &hostname)) {
found_addr = TRUE;
i++;
} else {
NlsPutMsg(STDOUT, PING6_MESSAGE_10, arg);
// printf("Bad IPv6 address %s.\n", arg);
exit(1);
}
}
}
}
if (!found_addr) {
NlsPutMsg(STDOUT, PING6_MESSAGE_13);
// printf("IPv6 address must be specified.\n");
exit(1);
}
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) {
NlsPutMsg(STDOUT, PING6_MESSAGE_14,
GetLastError() );
// printf("Unable to contact IPv6 driver, error code %d.\n",
// GetLastError() );
exit(1);
}
if (srcaddr.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, PING6_MESSAGE_15,
WSAGetLastError());
// printf("Unable to create IPv6 socket, error code %d.\n",
// WSAGetLastError());
exit(1);
}
//
// This ioctl will fail if ping6 is run on a system
// without the proper support in AFD and the stack.
// In that case, srcaddr will be unchanged.
//
(void) WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY,
&dstaddr, sizeof dstaddr,
&srcaddr, sizeof srcaddr,
&BytesReturned, NULL, NULL);
closesocket(s);
}
request = LocalAlloc(LMEM_FIXED, sizeof *request + SendSize);
if (request == NULL) {
NlsPutMsg(STDOUT, PING6_MESSAGE_16);
// printf("Unable to allocate required memory.\n");
exit(1);
}
SendBuffer = (char *)(request + 1);
//
// Calculate receive buffer size and try to allocate it.
//
if (SendSize <= DEFAULT_SEND_SIZE) {
RcvSize = DEFAULT_BUFFER_SIZE;
}
else {
RcvSize = MAX_BUFFER_SIZE;
}
reply = LocalAlloc(LMEM_FIXED, sizeof *reply + RcvSize);
if (reply == NULL) {
NlsPutMsg(STDOUT, PING6_MESSAGE_16);
// printf("Unable to allocate required memory.\n");
exit(1);
}
RcvBuffer = (char *)(reply + 1);
//
// Initialize the request buffer.
//
CopyTDIFromSA6(&request->DstAddress, &dstaddr);
CopyTDIFromSA6(&request->SrcAddress, &srcaddr);
request->Flags = 0;
if (Reverse)
request->Flags |= ICMPV6_ECHO_REQUEST_FLAG_REVERSE;
request->Timeout = Timeout;
request->TTL = 0; // default TTL
//
// Initialize the request data pattern.
//
for (i = 0; i < SendSize; i++)
SendBuffer[i] = 'a' + (i % 23);
if (hostname)
NlsPutMsg(STDOUT, PING6_MESSAGE_17, hostname, format_addr(&dstaddr));
// printf("\nPinging %s [%s]", hostname, format_addr(&dstaddr));
else
NlsPutMsg(STDOUT, PING6_MESSAGE_18, format_addr(&dstaddr));
// printf("\nPinging %s", format_addr(&dstaddr));
if (srcaddr.sin6_family != 0)
NlsPutMsg(STDOUT, PING6_MESSAGE_19, format_addr(&srcaddr));
// printf("\nfrom %s", format_addr(&srcaddr));
NlsPutMsg(STDOUT, PING6_MESSAGE_20, SendSize);
// printf(" with %u bytes of data:\n\n", SendSize);
SetConsoleCtrlHandler(&ConsoleControlHandler, TRUE);
for (i = 0; i < Count; i++) {
if (! DeviceIoControl(Handle,
IOCTL_ICMPV6_ECHO_REQUEST,
request,
sizeof *request + SendSize,
reply, sizeof *reply + RcvSize,
&bytesReturned,
NULL) ||
(bytesReturned < sizeof *reply)) {
errorCode = GetLastError();
if (errorCode < IP_STATUS_BASE) {
NlsPutMsg(STDOUT, PING6_MESSAGE_21, errorCode);
// printf("PING: transmit failed, error code %u\n", errorCode);
}
else {
NlsPutMsg(STDOUT, PING6_MESSAGE_22,
GetErrorString(errorCode));
// printf("PING: %s\n", GetErrorString(errorCode));
}
}
else {
struct sockaddr_in6 ReplyAddress;
ReplySize = bytesReturned - sizeof *reply;
CopySAFromTDI6(&ReplyAddress, &reply->Address);
num_send++;
if (! IN6_IS_ADDR_UNSPECIFIED(&ReplyAddress.sin6_addr))
NlsPutMsg(STDOUT, PING6_MESSAGE_23, format_addr(&ReplyAddress));
// printf("Reply from %s: ", format_addr(&ReplyAddress));
if (reply->Status == IP_SUCCESS) {
NlsPutMsg(STDOUT, PING6_MESSAGE_24, ReplySize);
// printf("bytes=%u ", ReplySize);
if (ReplySize != SendSize) {
NlsPutMsg(STDOUT, PING6_MESSAGE_25, SendSize);
// printf("(sent %u) ", SendSize);
}
else {
char *sendptr, *recvptr;
sendptr = SendBuffer;
recvptr = RcvBuffer;
for (j = 0; j < SendSize; j++)
if (*sendptr++ != *recvptr++) {
NlsPutMsg(STDOUT, PING6_MESSAGE_26, j);
// printf("- MISCOMPARE at offset %u - ", j);
break;
}
}
if (reply->RoundTripTime) {
NlsPutMsg(STDOUT, PING6_MESSAGE_27, reply->RoundTripTime);
// printf("time=%ums ", reply->RoundTripTime);
}
else {
NlsPutMsg(STDOUT, PING6_MESSAGE_28);
// printf("time<1ms ");
}
NlsPutMsg(STDOUT, PING6_MESSAGE_29);
// printf("\n");
//
// Collect statistics.
//
num_recv++;
time_total += reply->RoundTripTime;
if (reply->RoundTripTime < time_min)
time_min = reply->RoundTripTime;
if (reply->RoundTripTime > time_max)
time_max = reply->RoundTripTime;
}
else {
NlsPutMsg(STDOUT, PING6_MESSAGE_30,
GetErrorString(reply->Status));
// printf("%s\n", GetErrorString(reply->Status));
if (IN6_IS_ADDR_UNSPECIFIED(&ReplyAddress.sin6_addr)) {
//
// Diagnose some common errors.
//
if (reply->Status == IP_DEST_NO_ROUTE) {
if (IN6_IS_ADDR_LINKLOCAL(&dstaddr.sin6_addr) ||
IN6_IS_ADDR_SITELOCAL(&dstaddr.sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&dstaddr.sin6_addr))
NlsPutMsg(STDOUT, PING6_MESSAGE_31);
// printf(" Specify correct scope-id or "
// "use -s to specify source address.\n");
}
else if (reply->Status == IP_PARAMETER_PROBLEM) {
if (IN6_IS_ADDR_UNSPECIFIED(&dstaddr.sin6_addr))
NlsPutMsg(STDOUT, PING6_MESSAGE_32);
// printf(" Illegal destination address.\n");
else if (dstaddr.sin6_scope_id != 0)
NlsPutMsg(STDOUT, PING6_MESSAGE_33);
// printf(" Invalid scope-id specified.\n");
else if (IN6_IS_ADDR_LINKLOCAL(&dstaddr.sin6_addr) ||
IN6_IS_ADDR_SITELOCAL(&dstaddr.sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&dstaddr.sin6_addr))
NlsPutMsg(STDOUT, PING6_MESSAGE_31);
// printf(" Specify correct scope-id or "
// "use -s to specify source address.\n");
}
else if (reply->Status == IP_BAD_ROUTE) {
NlsPutMsg(STDOUT, PING6_MESSAGE_34);
// printf(" Problem with source address or scope-id.\n");
}
}
}
if (i < (Count - 1)) {
if (reply->RoundTripTime < MIN_INTERVAL) {
Sleep(MIN_INTERVAL - reply->RoundTripTime);
}
}
}
}
print_statistics();
return num_recv == 0;
}