2025-11-28 00:35:46 +09:00

579 lines
19 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "Utils.h"
#include <new>
//********************************************************************************************
// Function: FlushCurrentLine
//
// Description: Clears any input lingering in the STDIN buffer
//
//********************************************************************************************
void FlushCurrentLine()
{
int i;
while ((i = getc(stdin)) != EOF && i != '\n')
{
continue;
}
}
//********************************************************************************************
// Function: GetPreferredAddress
//
// Description: This function sorts a list of Ipv4 & Ipv6 addresses, and returns the "best" address that stack determines
//
//********************************************************************************************
DWORD GetPreferredAddress(_In_ DWORD numElement,
_In_reads_(numElement) SOCKADDR_IN6 *pAddrList,
_Out_ SOCKADDR_STORAGE *pPreferredAddr)
{
DWORD dwErr = ERROR_SUCCESS;
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData = {0};
SOCKET socketIoctl = INVALID_SOCKET;
SOCKET_ADDRESS_LIST *pSocketAddrList = NULL;
SOCKADDR_IN6 *pBestAddress = NULL;
DWORD dwSize = 0;
DWORD dwBytes = 0;
DWORD i = 0;
BOOL fWSAStarted = FALSE;
ZeroMemory (pPreferredAddr, sizeof(SOCKADDR_STORAGE));
//This do--while(FALSE) loop is used to break in between based on error conditions
do
{
// Initialize WinSock
dwErr = WSAStartup(wVersionRequested, &wsaData);
if (dwErr != ERROR_SUCCESS)
{
wprintf(L"WSAStartup failed, (dwErr = %#x).", dwErr);
break;
}
fWSAStarted = TRUE;
// create socket
socketIoctl = WSASocket(AF_INET6, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (socketIoctl == INVALID_SOCKET)
{
dwErr = WSAGetLastError();
wprintf(L"WSASocket failed, (dwErr = %#x).", dwErr);
break;
}
dwSize = SIZEOF_SOCKET_ADDRESS_LIST(numElement);
pSocketAddrList = reinterpret_cast<SOCKET_ADDRESS_LIST *>(new BYTE[dwSize]);
if (pSocketAddrList == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
wprintf(L"out of memory, (dwErr = %#x).", dwErr);
break;
}
for (i = 0; i < numElement; i++ )
{
pSocketAddrList->Address[i].lpSockaddr = (LPSOCKADDR)(pAddrList + i);
pSocketAddrList->Address[i].iSockaddrLength = sizeof(SOCKADDR_IN6);
}
pSocketAddrList->iAddressCount = numElement;
// sort addresses
dwErr = WSAIoctl(
socketIoctl,
SIO_ADDRESS_LIST_SORT,
(LPVOID)pSocketAddrList,
dwSize,
(LPVOID)pSocketAddrList,
dwSize,
&dwBytes,
NULL,
NULL
);
if (dwErr == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
wprintf(L"WSAIoctl sort address failed, (dwErr = %#x).", dwErr);
break;
}
pBestAddress = reinterpret_cast<SOCKADDR_IN6 *>(pSocketAddrList->Address[0].lpSockaddr);
//If mapped IPv6, retrieve the original IPv4 dest address
if (IN6ADDR_ISV4MAPPED(pBestAddress))
{
pPreferredAddr->ss_family = AF_INET;
(reinterpret_cast<SOCKADDR_IN *> (pPreferredAddr))->sin_addr = *((PIN_ADDR) IN6_GET_ADDR_V4MAPPED(&(pBestAddress->sin6_addr)));
(reinterpret_cast<SOCKADDR_IN *> (pPreferredAddr))->sin_port = pBestAddress->sin6_port;
}
else
{
pPreferredAddr->ss_family = AF_INET6;
(reinterpret_cast<SOCKADDR_IN6 *> (pPreferredAddr))->sin6_addr = pBestAddress->sin6_addr;
(reinterpret_cast<SOCKADDR_IN6 *> (pPreferredAddr))->sin6_scope_id = pBestAddress->sin6_scope_id;
(reinterpret_cast<SOCKADDR_IN6 *> (pPreferredAddr))->sin6_port = pBestAddress->sin6_port;
}
}while(FALSE);
if (socketIoctl != INVALID_SOCKET)
{
closesocket(socketIoctl);
socketIoctl = INVALID_SOCKET;
}
if (fWSAStarted)
{
WSACleanup();
}
if (pSocketAddrList)
{
delete[] reinterpret_cast<BYTE *>(pSocketAddrList);
}
return dwErr;
}
//********************************************************************************************
// Function: ConvertStringToSockAddr
//
// Description: Converts destination hostname or URL to IP address
//
//********************************************************************************************
HRESULT ConvertStringToSockAddr(_In_ LPWSTR destIPAddr, _Out_ SOCKADDR_STORAGE *socketAddress)
{
HRESULT hr = S_OK;
DWORD dwErr = NO_ERROR;
struct addrinfoW *result = NULL;
struct addrinfoW *ptr = NULL;
WSADATA wsaData;
int count = 0;
int addrCount = 0;
SOCKADDR_IN6 *destAddrList = NULL;
SOCKADDR_IN *pDestSockAddrIn = NULL;
BOOL fWSAStarted = FALSE;
//This do--while(FALSE) loop is used to break in between based on error conditions
do
{
//Intialize socket, to use GetAddrInfoW()
dwErr = WSAStartup (SOCKET_VERSION_REQUESTED,&wsaData);
if (dwErr != NO_ERROR)
{
hr = HRESULT_FROM_WIN32(dwErr);
break;
}
fWSAStarted = TRUE;
//get Destination IP address from Destination address string hostname or URL
dwErr = GetAddrInfoW(destIPAddr,NULL,NULL,&result);
if (dwErr == NO_ERROR)
{
//Get the number of socket addresses returned by GetAddrInfoW
for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
{
addrCount ++;
}
destAddrList = new (std::nothrow) SOCKADDR_IN6[addrCount];
ZeroMemory (destAddrList, (sizeof(SOCKADDR_IN6) * addrCount));
for (ptr = result; ((ptr != NULL) && (count<addrCount)) ; ptr = ptr->ai_next)
{
if (ptr->ai_family == AF_INET)
{
//Map IPv4 to mapped IPV6 to sort consistent IPv6 list using CreateSortedAddressPairs()
pDestSockAddrIn = reinterpret_cast<SOCKADDR_IN *>(ptr->ai_addr);
IN6ADDR_SETV4MAPPED(&(destAddrList[count]),reinterpret_cast<IN_ADDR *>(&(pDestSockAddrIn->sin_addr)),
IN4ADDR_SCOPE_ID(pDestSockAddrIn), pDestSockAddrIn->sin_port);
}
else if (ptr->ai_family == AF_INET6)
{
destAddrList[count] = *(reinterpret_cast<SOCKADDR_IN6 *>(result->ai_addr));
}
else
{
hr = E_UNEXPECTED;
wprintf(L"Unsupported IP family, please enter IPv4 or IPv6 destination\n");
break;
}
count++;
}
// determine the preferred IP address
dwErr = GetPreferredAddress(addrCount,
destAddrList,
socketAddress);
if (dwErr != NO_ERROR)
{
wprintf(L"WSAIoctl failed, (dwErr = %#x).", dwErr );
hr = HRESULT_FROM_WIN32(dwErr);
}
}
else
{
hr = HRESULT_FROM_WIN32(dwErr);
}
}while (FALSE);
if (fWSAStarted)
{
WSACleanup();
}
pDestSockAddrIn = NULL;
delete [] destAddrList;
ptr = NULL;
FreeAddrInfoW(result);
DisplayError(hr);
return hr;
}
//********************************************************************************************
// Function: GetInterfaceType
//
// Description: Gets the interface type for each connection
//
//********************************************************************************************
void GetInterfaceType (_In_ GUID interfaceGUID, _In_ HRESULT hr)
{
NET_LUID interfaceLUID = {0};
MIB_IF_ROW2 mib;
if (hr == S_OK)
{
// Get interface LUID
DWORD dwError = ConvertInterfaceGuidToLuid(&interfaceGUID,&interfaceLUID);
if (dwError == NO_ERROR)
{
//Get interface info entry
mib.InterfaceLuid = interfaceLUID;
dwError= GetIfEntry2(&mib);
if (dwError == NO_ERROR)
{
// Get interface type
wprintf(L"Connection Interface : %ws\n", mib.Description);
}
}
hr = HRESULT_FROM_WIN32(dwError);
}
DisplayError(hr);
}
//********************************************************************************************
// Function: IsDataPlanStatusAvailable
//
// Description: Checks if the data plan status values are default values, or provided by the MNO
//
//********************************************************************************************
BOOL IsDataPlanStatusAvailable(_In_ const NLM_DATAPLAN_STATUS *pDataPlanStatus)
{
BOOL isAvailable = FALSE;
//
// usage data is valid only if both planUsage and lastUpdatedTime are valid
//
if (pDataPlanStatus->UsageData.UsageInMegabytes != NLM_UNKNOWN_DATAPLAN_STATUS
&& (pDataPlanStatus->UsageData.LastSyncTime.dwHighDateTime != 0
|| pDataPlanStatus->UsageData.LastSyncTime.dwLowDateTime != 0))
{
isAvailable = TRUE;
}
else if (pDataPlanStatus->DataLimitInMegabytes != NLM_UNKNOWN_DATAPLAN_STATUS)
{
isAvailable = TRUE;
}
else if (pDataPlanStatus->InboundBandwidthInKbps != NLM_UNKNOWN_DATAPLAN_STATUS)
{
isAvailable = TRUE;
}
else if (pDataPlanStatus->OutboundBandwidthInKbps != NLM_UNKNOWN_DATAPLAN_STATUS)
{
isAvailable = TRUE;
}
else if (pDataPlanStatus->NextBillingCycle.dwHighDateTime != 0
|| pDataPlanStatus->NextBillingCycle.dwLowDateTime != 0)
{
isAvailable = TRUE;
}
else if (pDataPlanStatus->MaxTransferSizeInMegabytes != NLM_UNKNOWN_DATAPLAN_STATUS)
{
isAvailable = TRUE;
}
return isAvailable;
}
//********************************************************************************************
// Function: DisplayCost
//
// Description: Displays meaningful cost values to the user
//
//********************************************************************************************
void DisplayCostDescription (_In_ DWORD cost)
{
if (cost == NLM_CONNECTION_COST_UNKNOWN)
{
wprintf(L"Cost : Unknown\n");
}
else if (cost & NLM_CONNECTION_COST_UNRESTRICTED)
{
wprintf(L"Cost : Unrestricted\n");
}
else if (cost & NLM_CONNECTION_COST_FIXED)
{
wprintf(L"Cost : Fixed\n");
}
else if (cost & NLM_CONNECTION_COST_VARIABLE)
{
wprintf(L"Cost : Variable\n");
}
if (cost & NLM_CONNECTION_COST_OVERDATALIMIT)
{
wprintf(L"OverDataLimit : Yes\n");
}
else
{
wprintf(L"OverDataLimit : No\n");
}
if (cost & NLM_CONNECTION_COST_APPROACHINGDATALIMIT)
{
wprintf(L"Approaching DataLimit : Yes\n");
}
else
{
wprintf(L"Approaching DataLimit : No\n");
}
if (cost & NLM_CONNECTION_COST_CONGESTED)
{
wprintf(L"Congested : Yes\n");
}
else
{
wprintf(L"Congested : No\n");
}
if (cost & NLM_CONNECTION_COST_ROAMING)
{
wprintf(L"Roaming : Yes\n");
}
else
{
wprintf(L"Roaming : No\n");
}
}
//********************************************************************************************
// Function: DisplayDataPlanStatus
//
// Description: Displays data plan status values to the user
//
//********************************************************************************************
void DisplayDataPlanStatus (_In_ const NLM_DATAPLAN_STATUS *pDataPlanStatus)
{
if (FALSE == IsDataPlanStatusAvailable(pDataPlanStatus))
{
wprintf(L"Plan Data usage unknown\n");
}
else
{
WCHAR szGuid[39]={0};
StringFromGUID2(pDataPlanStatus->InterfaceGuid, szGuid, 39);
wprintf(L"Interface ID : %s\n",szGuid);
//check for default or unknown value
if (pDataPlanStatus->UsageData.UsageInMegabytes != NLM_UNKNOWN_DATAPLAN_STATUS)
{
wprintf(L"Data Usage in Megabytes : %d\n", pDataPlanStatus->UsageData.UsageInMegabytes);
}
if ((pDataPlanStatus->UsageData.LastSyncTime.dwHighDateTime != 0) || (pDataPlanStatus->UsageData.LastSyncTime.dwLowDateTime != 0))
{
wprintf(L"Data Usage Synced Time : ");
PrintFileTime (pDataPlanStatus->UsageData.LastSyncTime);
}
if (pDataPlanStatus->DataLimitInMegabytes != NLM_UNKNOWN_DATAPLAN_STATUS)
{
wprintf(L"Data Limit in Megabytes : %d\n", pDataPlanStatus->DataLimitInMegabytes);
}
if (pDataPlanStatus->InboundBandwidthInKbps != NLM_UNKNOWN_DATAPLAN_STATUS)
{
wprintf(L"Inbound Bandwidth in Kbps : %d\n", pDataPlanStatus->InboundBandwidthInKbps);
}
if (pDataPlanStatus->OutboundBandwidthInKbps != NLM_UNKNOWN_DATAPLAN_STATUS)
{
wprintf(L"Outbound Bandwidth in Kbps : %d\n", pDataPlanStatus->OutboundBandwidthInKbps);
}
if ((pDataPlanStatus->NextBillingCycle.dwHighDateTime != 0) || (pDataPlanStatus->NextBillingCycle.dwLowDateTime != 0))
{
wprintf(L"Next Billing Cycle : ");
PrintFileTime (pDataPlanStatus->NextBillingCycle);
}
if (pDataPlanStatus->MaxTransferSizeInMegabytes != NLM_UNKNOWN_DATAPLAN_STATUS)
{
wprintf(L"Maximum Transfer Size in Megabytes : %d\n", pDataPlanStatus->MaxTransferSizeInMegabytes);
}
}
}
//********************************************************************************************
// Function: PrintFileTime
//
// Description: Converts file time to local time, to display to the user
//
//********************************************************************************************
void PrintFileTime(_In_ FILETIME time)
{
SYSTEMTIME stUTC, stLocal;
// Convert filetime to local time.
FileTimeToSystemTime(&time, &stUTC);
SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
wprintf(L"%02d/%02d/%d %02d:%02d:%02d\n",
stLocal.wMonth, stLocal.wDay, stLocal.wYear,stLocal.wHour, stLocal.wMinute, stLocal.wSecond);
}
//********************************************************************************************
// Function: DisplayError
//
// Description: Maps common HRESULTs to descriptive error strings
//
//********************************************************************************************
void DisplayError(_In_ HRESULT hr)
{
struct errorDescription
{
HRESULT hr;
PWSTR description;
} validErrors[] =
{
{E_OUTOFMEMORY, L"Error: Failed to allocate necessary memory"},
{E_NOINTERFACE, L"Error: Network cost API is only supported on WIN8 client machine."},
{E_POINTER, L"Error: Pointer is not valid"},
{E_PENDING, L"Error: In the process of determining cost. If you already registered for cost change, notification will be raised when cost change is determined"},
{HRESULT_FROM_WIN32(ERROR_NO_NETWORK), L"Error: Machine might have lost connectivity, No network found"},
};
if (hr != S_OK)
{
for(int eindex=0;
eindex < sizeof(validErrors) / sizeof(struct errorDescription);
eindex++ )
{
if (hr == validErrors[eindex].hr)
{
wprintf(L"%s\n",validErrors[eindex].description);
return;
}
}
wprintf(L"Error Code: 0x%x\n",hr);
}
}
//********************************************************************************************
// Function: GetConnectionFromGUID
//
// Description: Gets the connection type from the connection GUID
//
//********************************************************************************************
HRESULT GetConnectionFromGUID(_In_ INetworkListManager *pManager, _In_ GUID connID, _Out_ INetworkConnection **ppConnection)
{
HRESULT hr = S_OK;
BOOL bFound = FALSE;
BOOL bDone = FALSE;
ULONG cFetched = 0;
CComPtr<IEnumNetworkConnections> pNetworkConnections;
GUID guid;
*ppConnection = NULL;
hr = pManager->GetNetworkConnections(&pNetworkConnections);
if (SUCCEEDED(hr))
{
while (!bDone)
{
CComPtr<INetworkConnection> pConnection;
hr = pNetworkConnections->Next(1, &pConnection, &cFetched);
if (SUCCEEDED(hr) && cFetched > 0)
{
hr = pConnection->GetConnectionId(&guid);
if (SUCCEEDED(hr))
{
if (IsEqualGUID(guid,connID))
{
*ppConnection = pConnection;
(*ppConnection)->AddRef();
bFound = TRUE;
break;
}
}
}
else
{
bDone = TRUE;
}
}
if (!bFound)
{
hr = E_FAIL;
}
}
return hr;
}
//********************************************************************************************
// Function: GetDestinationAddress
//
// Description: Gets Destination String and converts it to socket address
//
//********************************************************************************************
HRESULT GetDestinationAddress(_Out_ DESTINATION_INFO *pDestIPAddr)
{
HRESULT hr;
WCHAR destAddress[IP_ADDRESS_SIZE] = {0};
NLM_SOCKADDR destSocketAddress = {0};
ZeroMemory (pDestIPAddr, sizeof(DESTINATION_INFO));
//The feature allow registration for multiple destination addresses, the sample SDK restricts to one Destination address
wprintf(L"Please enter the destination address :\n");
wscanf_s(L"%s",destAddress,IP_ADDRESS_SIZE-1);
//convert destination addr string to numeric IP address
hr = ConvertStringToSockAddr(destAddress, reinterpret_cast<SOCKADDR_STORAGE *>(&destSocketAddress));
if (hr == S_OK)
{
//enter the destination addr string and the numeric IP address to structure, to retrieve the string later for display info
wmemcpy(pDestIPAddr->addrString, destAddress, IP_ADDRESS_SIZE);
pDestIPAddr->ipAddr = destSocketAddress;
}
return hr;
}