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

1332 lines
40 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) 1985-2005 Microsoft Corporation. All Rights Reserved.
Abstract:
This C file includes sample code for publishing and resolving
a peer name using the Peer Name Resolution Protocol APIs
--********************************************************************/
#pragma warning(disable:4201) // nameless struct/union
#include <p2p.h>
#include <stdio.h>
#include <strsafe.h>
#include "pnrp.h"
#define ERRBUFSIZE 1024
#define HRESULTTOWIN32(hres) \
((HRESULT_FACILITY(hres) == FACILITY_WIN32) \
? HRESULT_CODE(hres) \
: (hres))
#define FLUSH_AND_GET_RESPONSE(hr, wzBuff) \
fflush(stdin); \
hr = StringCbGets(wzBuff, sizeof(wzBuff));
// If Count is not already aligned, then round it up until it is.
//
// Global Handles
HANDLE g_hAsyncResolve = NULL;
HANDLE g_hRegistration = NULL;
// Global registration info - Since updating a peer name registration will
// overwrite all previous registration info, registration information
// must be stored so that a user does not have to reenter item that he
// or she does not wish to change during an update
PEER_PNRP_REGISTRATION_INFO g_RegInfo = {0};
SOCKADDR_IN6 g_saRegAddrsV6[NUM_USER_DEFINED_ADDRESSES] = {0};
SOCKADDR_IN g_saRegAddrsV4[NUM_USER_DEFINED_ADDRESSES] = {0};
WCHAR g_wzCloudName[MAX_CLOUD_NAME] = {0};
WCHAR g_wzComment[MAX_COMMENT_LENGTH] = {0};
WCHAR g_PayloadData[MAX_PAYLOAD_LENGTH] = {0};
BOOL g_fUseV6 = TRUE;
// Custom menu variables, types, and structs
// -------------------------------------
// This defines the signature for functions
// executed by menu selections
typedef HRESULT (*COMMAND)();
// This struct groups command titles (e.g. "Register Peer Name")
// with their corresponding function (e.g. RegisterPeerName)
typedef struct _MENU_COMMAND
{
PWSTR pwzTitle;
COMMAND pfnCommand;
} MENU_COMMAND, *PMENU_COMMAND;
// This menu lists commands (and their corresponding functions)
// when no peer name is currently registered
MENU_COMMAND g_MenuUnregistered[] =
{
{L"Register Peer Name", RegisterPeerNameCommand},
{L"Resolve Peer Name", ResolvePeerNameCommand},
{L"Show DNS encoded name", ShowDNSEncodedNameCommand},
{L"List Clouds", EnumCloudsCommand},
{L"Exit", NULL}
};
// This menu lists commands (and their corresponding functions)
// when a peer name is currently registered
MENU_COMMAND g_MenuRegistered[] =
{
{L"Update Registration", UpdateRegistrationCommand},
{L"Delete Registration", UnregisterPeerNameCommand},
{L"Resolve Peer Name", ResolvePeerNameCommand},
{L"Show DNS encoded name", ShowDNSEncodedNameCommand},
{L"List Clouds", EnumCloudsCommand},
{L"Exit", NULL}
};
//-------------------------
// Registration Functions
//-------------------------
// The following functions involve filling out the PEER_PNRP_REGISTRATION_INFO
// struct, which has the following members:
//
// PWSTR pwzCloudName : Name of cloud in which to register. A value of
// NULL will register the peer name in all clouds.
// (PWSTR) PEER_PNRP_ALL_LINK_CLOUDS can be used to
// register in all LinkLocal clouds
//
// PWSTR pwzPublishingIdentity : name of identity used to register (optional)
//
// ULONG cAddresses : Number of addresses to be registered. Can also
// use value PEER_PNRP_AUTO_ADDRESSES to have the
// addresses automatically selected
//
// SOCKADDR **ppAddresses : Array of pointers to SOCKADDR or SOCKADDR_IN6
// structs which contain information about the
// addresses used to register
//
// WORD wPort : Port number on which to register (optional)
//
// PWSTR pwzComment : User specified string that is retrievable by the
// resolver (optional)
//
// PEER_DATA payload : Exteneded info about peer (optional) that is also
// retrievable by the resolver.
// ULONG cbdata : Length of payload
// PBYTE pbData : Pointer to data
//
// NOTE: You may also call PeerPnrpRegister with NULL in place of this struct,
// in which case the peer name will be registered in all clouds, with default
// addresses and port, and no comment or payload. You may not update a
// registration will a NULL registration info struct. You do not need a
// PEER_PNRP_REGISTRATION_INFO struct to unregister a peer name.
//-------------------------
//-----------------------------------------------------------------------------
// Function: PrintError
// Purpose: Prints the error and the associated error string
// Parameters: hr error obtained
//
void PrintError(HRESULT hrError)
{
DWORD dwCch=0;
WCHAR wszBuffer[ERRBUFSIZE] = {0};
if (HRESULT_FACILITY(hrError) == FACILITY_P2P)
{
HMODULE hResDll = GetModuleHandle(L"p2p.dll");
if (NULL != hResDll)
{
dwCch = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
hResDll,
HRESULTTOWIN32(hrError),
0,
wszBuffer,
ERRBUFSIZE,
NULL);
FreeLibrary(hResDll);
if (dwCch > 0)
{
wprintf(L"Error Description: %s\n", wszBuffer);
}
}
}
else
{
dwCch = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
HRESULTTOWIN32(hrError),
0,
wszBuffer,
ERRBUFSIZE,
NULL);
if (dwCch > 0)
{
wprintf(L"Error Description: %s\n", wszBuffer);
}
}
}
//-----------------------------------------------------------------------------
// Function: RegisterPeerNameCommand
// Purpose: Prompts user for registration information, fills out a
// PEER_PNRP_REGISTRATION_INFO struct, creates a peer name,
// and calls PeerPnrpRegister to register the peer name
// Parameters: None
//
// NOTE: The signature of this function must adhere to the MENU_COMMAND typedef
HRESULT RegisterPeerNameCommand()
{
WCHAR wzInputBuffer[256] = {0};
WCHAR wzIdentity[MAX_PEERNAME_LENGTH] = {0};
WCHAR wzClassifier[MAX_CLASSIFIER_LENGTH] = {0};
HRESULT hr = S_OK;
PWSTR pwzPeerName = NULL;
ULONG cAddresses = 0;
//ppRegAddrs is a pointer to an array of pointers to SOCKADDR structures
SOCKADDR** ppRegAddrs = NULL;
ZeroMemory(&g_RegInfo, sizeof(PEER_PNRP_REGISTRATION_INFO));
//Collect Registration Information
//---------------------------------
// Addresses
hr = GetAddress(TRUE, &cAddresses, &ppRegAddrs);
if (SUCCEEDED(hr))
{
// Cloud name
hr = GetCloudName(cAddresses != 1, celems(g_wzCloudName), g_wzCloudName);
}
if (SUCCEEDED(hr))
{
// Comment
hr = GetComment(celems(g_wzComment), g_wzComment);
}
if (SUCCEEDED(hr))
{
// Payload
hr = GetPayload(sizeof(g_PayloadData), (PBYTE)g_PayloadData);
if (cAddresses == 0)
{
// If no addresses were specified, the payload must be non-null.
while (PayloadSize(g_PayloadData) == 0 && SUCCEEDED(hr))
{
wprintf(L"Payload must be non-empty if no addresses are specified.\n");
hr = GetPayload(sizeof(g_PayloadData), (PBYTE)g_PayloadData);
}
}
}
// Create Peer Name
//------------------
// If secure, get identity
if (SUCCEEDED(hr))
{
wprintf(L"Secured peer name [no]: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (SUCCEEDED(hr) && _wcsicmp(wzInputBuffer, L"yes") == 0)
{
hr = GetIdentity(celems(wzIdentity), wzIdentity);
}
}
// Classifier
if (SUCCEEDED(hr))
{
do
{
wprintf(L"Classifier: ");
FLUSH_AND_GET_RESPONSE(hr, wzClassifier);
// Classifier must be non-null.
if (SUCCEEDED(hr) && wzClassifier[0] == L'\0')
{
wprintf(L"Classifier must be non-empty.\n");
}
} while (SUCCEEDED(hr) && wzClassifier[0] == L'\0');
}
// Create peer name
if (SUCCEEDED(hr))
{
if (wcslen(wzIdentity) != 0)
{
// Create secured peer name
hr = PeerCreatePeerName(wzIdentity, wzClassifier, &pwzPeerName);
}
else
{
// Create unsecured peer name
hr = PeerCreatePeerName(NULL, wzClassifier, &pwzPeerName);
}
}
// Fill out g_RegInfo struct
// -------------------------
if (wcslen(g_wzCloudName) == 0)
{
g_RegInfo.pwzCloudName = NULL;
}
else
{
g_RegInfo.pwzCloudName = g_wzCloudName;
}
//Copy in the address count and pointer to the array of pointers
g_RegInfo.cAddresses = cAddresses;
g_RegInfo.ppAddresses = ppRegAddrs;
g_RegInfo.pwzComment = g_wzComment;
if (PayloadSize(g_PayloadData) == 0)
{
g_RegInfo.payload.cbData = 0;
g_RegInfo.payload.pbData = NULL;
}
else
{
g_RegInfo.payload.cbData = PayloadSize(g_PayloadData);
g_RegInfo.payload.pbData = (PBYTE) g_PayloadData;
}
// Perform registration
if (SUCCEEDED(hr))
{
hr = PeerPnrpRegister(pwzPeerName, &g_RegInfo, &g_hRegistration);
}
// Display result
if (SUCCEEDED(hr))
{
wprintf(L"\nSuccessfully registered name: %s\n", pwzPeerName);
}
else
{
wprintf(L"\nError while registering name. HRESULT=0x%x\n", hr);
PrintError(hr);
}
if (pwzPeerName != NULL)
{
PeerFreeData(pwzPeerName);
}
if (ppRegAddrs != NULL)
{
free(ppRegAddrs);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: ResolvePnrpNameCommand
// Purpose: Asynchronously or synchronously resolves a user given peer name
// using PNRP in a user selected cloud (or clouds).
// Parameters: None
//
// NOTE: The signature of this function must adhere to the MENU_COMMAND typedef
HRESULT ResolvePeerNameCommand()
{
HRESULT hr = S_OK;
WCHAR wzInputBuffer[MAX_PEERNAME_LENGTH];
BOOL fSynch = TRUE;
WCHAR wzCloudName[MAX_CLOUD_NAME];
wprintf(L"\nNOTE: You cannot successfully resolve names registered by this process.\n");
wprintf(L" If you wish to test PNRP locally start another instance of this sample\n");
wprintf(L" application.\n");
// Prompts user to select a cloud in which to resolve
hr = GetCloudName(TRUE, celems(wzCloudName), wzCloudName);
// Asks user to select a synchronous or asynchronous resolve
if (SUCCEEDED(hr))
{
wprintf(L"Synchronous resolve [no]: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (_wcsicmp(wzInputBuffer, L"yes") != 0)
{
fSynch = FALSE;
}
do
{
wprintf(L"Peer Name (e.g. 0.myclassifier): ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
// Peer Name must be non-null.
if (SUCCEEDED(hr) && wzInputBuffer[0] == L'\0')
{
wprintf(L"Peer Name must be non-empty.\n");
}
} while (SUCCEEDED(hr) && wzInputBuffer[0] == L'\0');
}
if (SUCCEEDED(hr))
{
if (fSynch)
{
hr = SyncPeerNameResolve(wzInputBuffer, wzCloudName);
}
else
{
hr = AsyncPeerNameResolve(wzInputBuffer, wzCloudName);
}
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: ShowDNSEncodedNameCommand
// Purpose: Given a PNRP encoded name show the equivalant DNS encoded name to
// pass to GetAddrInfo to resolve this name. The DNS encoded name
// can be used to lookup a PNRP name from ANY application that
// calls GetAddrInfo (which means almost every application that
// converts a DNS name to an IP address). Try it from ping.exe or
// Internet Explorer.
// Parameters: None
//
// NOTE: The signature of this function must adhere to the MENU_COMMAND typedef
HRESULT ShowDNSEncodedNameCommand()
{
HRESULT hr = S_OK;
WCHAR wzInputBuffer[MAX_PEERNAME_LENGTH];
PWSTR pwzHostName = NULL;
do
{
wprintf(L"Peer Name (e.g. 0.myclassifier): ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
// Peer Name must be non-null.
if (SUCCEEDED(hr) && wzInputBuffer[0] == L'\0')
{
wprintf(L"Peer Name must be non-empty.\n");
}
} while (SUCCEEDED(hr) && wzInputBuffer[0] == L'\0');
hr = PeerNameToPeerHostName(wzInputBuffer, &pwzHostName);
if(FAILED(hr))
{
goto exit;
}
wprintf(L"\n");
wprintf(L"DNS encoded name is: %s\n", pwzHostName);
wprintf(L"\n");
wprintf(L"The DNS encoded name allows PNRP names to be used in ANY application\n");
wprintf(L"that calls GetAddrInfo to perform name resolution (and that is just about\n");
wprintf(L"every application).\n");
wprintf(L"\n");
wprintf(L"If this name is registered try using the DNS encoded form of the name in\n");
wprintf(L"ping or Internet Explorer.");
wprintf(L"\n\n");
exit:
PeerFreeData(pwzHostName);
pwzHostName = NULL;
return hr;
}
//-----------------------------------------------------------------------------
// Function: UpdateRegistrationCommand
// Purpose: Updates peer name registration information
// Parameters: None
//
// NOTE: The signature of this function must adhere to the MENU_COMMAND typedef
HRESULT UpdateRegistrationCommand()
{
HRESULT hr = S_OK;
//ppRegAddrs is a pointer to an array of pointers to SOCKADDR structures
SOCKADDR** ppRegAddrs = NULL;
ULONG cAddresses = 0;
// NOTE: You may not update cAddresses to or from
// PEER_PNRP_AUTO_ADDRESSES - this can only be specified at the
// initial registration
if (g_RegInfo.cAddresses != PEER_PNRP_AUTO_ADDRESSES)
{
// Get addresses
hr = GetAddress(FALSE, &cAddresses, &ppRegAddrs);
if (SUCCEEDED(hr))
{
//Copy in the address count and pointer to the array of pointers
g_RegInfo.cAddresses = cAddresses;
g_RegInfo.ppAddresses = ppRegAddrs;
}
}
// Update the comment - since the pointers in the g_RegInfo struct
// already point to g_wzComment, the comment in g_RegInfo will be
// implicitly updated
if (SUCCEEDED(hr))
{
hr = GetComment(celems(g_wzComment), g_wzComment);
}
// Update the payload. If no addresses are specified, ensure that the
// payload is non-empty
if (SUCCEEDED(hr))
{
hr = GetPayload(sizeof(g_PayloadData), (PBYTE)g_PayloadData);
if ((g_RegInfo.cAddresses == 0) && SUCCEEDED(hr))
{
while(PayloadSize(g_PayloadData) == 0 && SUCCEEDED(hr))
{
wprintf(L"Payload must be non-empty if no addresses are specified.\n");
hr = GetPayload(sizeof(g_PayloadData), (PBYTE)g_PayloadData);
}
}
}
if (SUCCEEDED(hr))
{
if (PayloadSize(g_PayloadData) == 0)
{
g_RegInfo.payload.cbData = 0;
g_RegInfo.payload.pbData = NULL;
}
else
{
g_RegInfo.payload.cbData = PayloadSize(g_PayloadData);
g_RegInfo.payload.pbData = (PBYTE) g_PayloadData;
}
}
// Perform the update and return the result
if (SUCCEEDED(hr))
{
hr = PeerPnrpUpdateRegistration(g_hRegistration, &g_RegInfo);
}
if (SUCCEEDED(hr))
{
wprintf(L"Registration successfully updated\n");
}
else
{
wprintf(L"Error while updating registration.\n");
}
if (ppRegAddrs != NULL)
{
free(ppRegAddrs);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: UnregisterPeerNameCommand
// Purpose: Unregisters the pnrp name
// Parameters: None
//
// NOTE: The signature of this function must adhere to the MENU_COMMAND typedef
HRESULT UnregisterPeerNameCommand()
{
HRESULT hr = S_OK;
hr = PeerPnrpUnregister(g_hRegistration);
if (SUCCEEDED(hr))
{
wprintf(L"Registration successfully deleted\n");
g_hRegistration = NULL;
}
else
{
wprintf(L"Registration could not be deleted\n");
}
return hr;
}
//--------------------------------------------------
// Helper Functions
//--------------------------------------------------
//-----------------------------------------------------------------------------
// Function: SyncPeerNameResolve
// Purpose: Wrapper function for PeerPnrpResolve that perform the
// resolution synchronously
// Parameters:
// pwzPeerName [in] : Peer Name to resolve
// pwzCloudName [in] : Cloud name in which to resolve
//
HRESULT SyncPeerNameResolve(PCWSTR pwzPeerName, PCWSTR pwzCloudName)
{
ULONG i = 0;
HRESULT hr = S_OK;
PPEER_PNRP_ENDPOINT_INFO pEndpointInfo = NULL;
ULONG cEndpoints = MAX_ENDPOINTS_TO_RESOLVE;
wprintf(L"Resolving (this may take a few seconds)...\n");
if (wcslen(pwzCloudName) != 0)
{
// Perform a synchronous resolve
hr = PeerPnrpResolve(pwzPeerName, pwzCloudName, &cEndpoints,
&pEndpointInfo);
}
else
{
// If no cloud name is given, search in all clouds (NULL argument)
hr = PeerPnrpResolve(pwzPeerName, NULL, &cEndpoints, &pEndpointInfo);
}
if (SUCCEEDED(hr))
{
// Display results
if (cEndpoints == 0)
{
wprintf(L"No endpoints found for peer name %s\n", pwzPeerName);
}
else
{
for (i = 0; i < cEndpoints; i++)
{
DisplayPNRPEndpoint(&pEndpointInfo[i]);
}
}
}
if (pEndpointInfo != NULL)
{
PeerFreeData(pEndpointInfo);
}
wprintf(L"Synchronous resolve complete.\n");
return hr;
}
//-----------------------------------------------------------------------------
// Function: AsyncPeerNameResolve
// Purpose: Wrapper function for PeerPnrpResolve that perform the
// resolution asynchronously
// Parameters:
// pwzPeerName [in] : Peer Name to resolve
// pwzCloudName [in] : Cloud name in which to resolve
//
HRESULT AsyncPeerNameResolve(PCWSTR pwzPeerName, PCWSTR pwzCloudName)
{
HRESULT hr = S_OK;
PPEER_PNRP_ENDPOINT_INFO pEndpointInfo = NULL;
ULONG cEndpoints = MAX_ENDPOINTS_TO_RESOLVE;
HANDLE hEvent;
HANDLE hResolve;
wprintf(L"Resolving (this may take a few seconds)...\n");
hEvent = CreateEvent(0, FALSE, FALSE, 0);
if (hEvent == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (wcslen(pwzCloudName) != 0)
{
// Start an asynchronous resolve
hr = PeerPnrpStartResolve(pwzPeerName, pwzCloudName, cEndpoints,
hEvent, &hResolve);
}
else
{
// If no cloud name is given, search in all clouds (NULL argument)
hr = PeerPnrpStartResolve(pwzPeerName, NULL, cEndpoints,
hEvent, &hResolve);
}
cEndpoints = 0;
if (SUCCEEDED(hr))
{
// Waits for up to 10 seconds for an endpoint to return
while (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)
{
hr = PeerPnrpGetEndpoint(hResolve, &pEndpointInfo);
if (hr == PEER_E_NO_MORE)
{
// Exit if all endpoints have been retrieved
break;
}
else if (SUCCEEDED(hr))
{
// Display results as they are received
hr = DisplayPNRPEndpoint(pEndpointInfo);
if (FAILED(hr))
{
wprintf(L"Error in DisplayEndpointInfo\n");
}
cEndpoints++;
if (pEndpointInfo != NULL)
{
PeerFreeData(pEndpointInfo);
}
}
else
{
break;
}
}
if (cEndpoints == 0)
{
wprintf(L"No endpoints found for peer name %s\n", pwzPeerName);
}
// End Asynchronous Resolve
hr = PeerPnrpEndResolve(hResolve);
wprintf(L"Asynchronous resolve complete.\n");
}
CloseHandle(hEvent);
return hr;
}
//-----------------------------------------------------------------------------
// Function: GetAddress
// Purpose: Prompts the user for a series of addresses
// Each address is converted to a SOCKADDR_IN6
// struct, and if that fails to a SOCKADDR_IN struct.
//
// Parameters:
// fAllowDefault [in] : controls whether we allow the user to use AUTO_ADDRESSES
// pcAddresses [out] : pointer to ULONG where the number of addresses will
// be returned.
// ppRegAddrOut [out] : pointer to where to write a pointer to an array of SOCKADDR
// pointers. The caller is responsible for freeing this pointer
// (the array of pointers and the structures they point to
// are all allocated in a single heap block. Only call
// free on the ppRegAddrOut pointer).
//
HRESULT GetAddress(BOOL fAllowDefault, __out ULONG* pcAddresses, __out_ecount(pcAddresses) SOCKADDR ***ppRegAddrOut)
{
INT iAddrSizeV6 = sizeof(SOCKADDR_IN6);
INT iAddrSizeV4 = sizeof(SOCKADDR_IN);
WCHAR wzInputBuffer[256] = {0};
INT cNumAddresses = 0;
INT nCurrentAddress = 0;
//We need to construct an array of pointers to SOCKADDR structures.
//ppRegAddrs will be a pointer to the array of pointers
SOCKADDR **ppRegAddrs = NULL;
HRESULT hr = S_OK;
// Prompt user to enter an address - although PNRP supports IPv4 addresses,
// this sample only supported IPv6 addresses. It is possible to create a
// registration with no addresses, but this requires specifying a non-null
// payload
if (fAllowDefault)
{
wprintf(L"Use default address [yes]: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (_wcsicmp(wzInputBuffer, L"no") != 0)
{
wprintf(L"Default address(es) selected.\n");
cNumAddresses = PEER_PNRP_AUTO_ADDRESSES;
}
}
if (cNumAddresses != PEER_PNRP_AUTO_ADDRESSES)
{
do
{
wprintf(L"Enter Number of Addresses to register: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (FAILED(hr))
{
goto exit;
}
cNumAddresses = _wtoi(wzInputBuffer);
if (cNumAddresses<0)
{
wprintf(L"Number of Addresses must be >= 0.\n");
}
else if (cNumAddresses==0)
{
wprintf(L"Registering 0 addresses. Payload is mandatory.\n");
}
} while (cNumAddresses < 0);
if (cNumAddresses == 0)
{
}
else
{
//Alocate enough space for our array of pointers and then
//the following array of structures
ppRegAddrs = (SOCKADDR**) malloc( sizeof(SOCKADDR*)*cNumAddresses + sizeof(SOCKADDR_IN6)*cNumAddresses);
for (nCurrentAddress = 0; nCurrentAddress < cNumAddresses;)
{
//Set the pointer ppRegAddrs[nCurrentAddress] to point to the address of the
//nCurrentAddress structure
ppRegAddrs[nCurrentAddress] = (SOCKADDR *)(((SOCKADDR_IN6 *)(ppRegAddrs + cNumAddresses))+ nCurrentAddress);
wprintf(L"Enter Address #%d\n", nCurrentAddress+1);
wprintf(L" IPV6 notation: e.g. 2001:0db8::3428:23ce\n");
wprintf(L" IPV4 notation: e.g. 192.168.1.50\n");
wprintf(L" > ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (FAILED(hr))
{
goto exit;
}
if (wcscmp(wzInputBuffer, L"") == 0)
{
wprintf(L"Address may not be empty.");
continue;
}
// Try parsing string as V6 address first
hr = WSAStringToAddressW(wzInputBuffer, AF_INET6,
NULL, (LPSOCKADDR) ppRegAddrs[nCurrentAddress],
(LPINT) &iAddrSizeV6);
if (FAILED(hr))
{
// Now try parsing as V4 address
hr = WSAStringToAddressW(wzInputBuffer, AF_INET,
NULL, (LPSOCKADDR) ppRegAddrs[nCurrentAddress],
(LPINT) &iAddrSizeV4);
if (SUCCEEDED(hr))
{
g_fUseV6 = FALSE;
}
else
{
hr = HRESULT_FROM_WIN32(WSAGetLastError());
wprintf(L"WSAStringToAddressW failed\n");
}
}
else
{
g_fUseV6 = TRUE;
}
//Move onto the next address
nCurrentAddress++;
}
}
}
exit:
if (SUCCEEDED(hr))
{
*pcAddresses = cNumAddresses;
*ppRegAddrOut = ppRegAddrs;
ppRegAddrs = NULL;
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: GetComment
// Purpose: Takes a string from the user and stores it at the location
// given by pwzComment. Returns HRESULT of the result
// Parameters:
// pwzComment [out] : pointer to location where comment should be copied
//
HRESULT GetComment(__in ULONG cchComment, __out_ecount(cchComment) PWSTR pwzComment)
{
HRESULT hr = S_OK;
wprintf(L"Comment [none]: ");
fflush(stdin);
hr = StringCbGets(pwzComment, cchComment);
return hr;
}
//-----------------------------------------------------------------------------
// Function: GetPayload
// Purpose: Copies user input into the location specified by pbPayloadData
// Parameters:
// pPayloadData [out]:pointer to location where payload data should be copied
//
HRESULT GetPayload(ULONG cbPayloadDataSize,
__out_ecount(cbPayloadDataSize) PBYTE pbPayloadData)
{
HRESULT hr = S_OK;
wprintf(L"Payload data [none]: ");
fflush(stdin);
hr = StringCbGets((WCHAR*) pbPayloadData, cbPayloadDataSize);
if (FAILED(hr))
{
ZeroMemory(pbPayloadData, cbPayloadDataSize);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: GetIdentity
// Purpose: Displays a list of available identities to the user, prompts
// for selection, and copies the selected identity's name to
// the parameter pwzIdentity
// Parameters:
// pwzIdentity [out] : pointer to string where retrieved identity's peer
// name will be stored
//
HRESULT GetIdentity(ULONG cchIdentity, __out_ecount(cchIdentity) PWSTR pwzIdentity)
{
HPEERENUM hIdentityEnum = NULL;
ULONG cIdentities = 0;
ULONG ulRegisterIdentity = 0;
PEER_NAME_PAIR** ppNamePairs = NULL;
WCHAR wzInputBuffer[256] = {0};
HRESULT hr = S_OK;
PWSTR pwzDefaultIdentity = NULL;
printf("Use default identity? [yes]: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (SUCCEEDED(hr) && _wcsicmp(wzInputBuffer, L"no") == 0)
{
// Retreieve list of available identities
hr = PeerEnumIdentities(&hIdentityEnum);
if (SUCCEEDED(hr))
{
hr = PeerGetItemCount(hIdentityEnum, &cIdentities);
if (SUCCEEDED(hr))
{
if (cIdentities == 0)
{
wprintf(L"No identities found on this machine\n");
hr = E_FAIL;
}
else
{
hr = PeerGetNextItem(hIdentityEnum, &cIdentities, (PVOID **) &ppNamePairs);
}
}
PeerEndEnumeration(hIdentityEnum);
}
// Display list of available identities
if (SUCCEEDED(hr))
{
ULONG i;
wprintf(L"\n");
for (i = 0; i < cIdentities; i++)
{
wprintf(L"%d) %s\n", i, ppNamePairs[i]->pwzFriendlyName);
}
// Retrieve user selection
wprintf(L"Identity to register: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (SUCCEEDED(hr))
{
ulRegisterIdentity = _wtoi(wzInputBuffer);
while (ulRegisterIdentity >= cIdentities)
{
wprintf(L"Invalid Selection\nSelect Identity: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (SUCCEEDED(hr))
{
ulRegisterIdentity = _wtoi(wzInputBuffer);
}
else
{
break;
}
}
if (SUCCEEDED(hr))
{
hr = StringCchCopy(pwzIdentity, cchIdentity,
ppNamePairs[ulRegisterIdentity]->pwzPeerName);
}
}
}
}
else
{
// Retrieve the Default Identity for this user
hr = PeerIdentityGetDefault(&pwzDefaultIdentity);
if (SUCCEEDED(hr))
{
hr = StringCchCopy(pwzIdentity, cchIdentity, pwzDefaultIdentity);
}
else
{
printf("Failed to get default identity. HRESULT=0x%x\n", hr);
PrintError(hr);
}
}
PeerFreeData(ppNamePairs);
PeerFreeData(pwzDefaultIdentity);
return hr;
}
//-----------------------------------------------------------------------------
// Function: GetCloudName
// Purpose: Prompts users to select a cloud (or set of clouds) from a list
// of available clouds and returns the name of the selected cloud
// Parameters:
// pwzCloudName [out] : pointer to string buffer to which cloud name is copied
//
// Returns: HRESULT
//
HRESULT GetCloudName(BOOL fAllowAll, __in ULONG cchCloudName, __out_ecount(cchCloudName) PWSTR pwzCloudName)
{
HRESULT hr = S_OK;
PPEER_PNRP_CLOUD_INFO pCloudInfo = {0};
ULONG ulCloud = 0;
ULONG cClouds = 0;
WCHAR wzInputBuffer[256] = {0};
hr = PeerPnrpGetCloudInfo(&cClouds, &pCloudInfo);
if (SUCCEEDED(hr))
{
ULONG i;
// Options include being able to select all available clouds,
// all Link Local clouds, or any individual cloud
wprintf(L"\nClouds:\n0) All Clouds\n1) All Link Local Clouds\n");
for (i = 0; i < cClouds; i++)
{
wprintf(L"%d) %s\n", i+2, pCloudInfo[i].pwzCloudName);
}
// Retrieve and verify user selection
wprintf(L"Select cloud [all clouds]: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (SUCCEEDED(hr))
{
ulCloud = _wtoi(wzInputBuffer);
while(ulCloud > cClouds + 1)
{
wprintf(L"Invalid Selection\nSelect Cloud [all clouds]: ");
FLUSH_AND_GET_RESPONSE(hr, wzInputBuffer);
if (SUCCEEDED(hr))
{
ulCloud = _wtoi(wzInputBuffer);
}
else
{
break;
}
}
}
if (!fAllowAll && ulCloud < 2)
{
printf("When an address is manually specified, you must also explicitly say which\n"
"cloud you wish to register in.\n");
PeerFreeData(pCloudInfo);
return E_INVALIDARG;
}
if (ulCloud == 0) // All Clouds
{
pwzCloudName[0] = (WCHAR) 0;
}
else if (ulCloud == 1) // All Link Local clouds
{
hr = StringCchCopy(pwzCloudName, cchCloudName,
PEER_PNRP_ALL_LINK_CLOUDS);
}
else
{
hr = StringCchCopy(pwzCloudName, cchCloudName,
pCloudInfo[ulCloud-2].pwzCloudName);
}
PeerFreeData(pCloudInfo);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: PayloadSize
// Purpose: Examines a WCHAR object and returns its length. NOTE: In this
// sample, payload data is implemented as a string. To use other
// types of data, you will need to modify this function to receive
// the appropriate data type and calculate its size accordingly
//
// Parameters:
// pPayloadData [in] : pointer to WCHAR object
//
// Returns: length of WCHAR string
//
ULONG PayloadSize(PWSTR pPayloadData)
{
ULONG cbPayload = 0;
cbPayload = (ULONG) wcslen(pPayloadData) * sizeof(WCHAR);
// Add sizeof(WCHAR) to account for NULL terminator.
if (cbPayload != 0)
{
cbPayload = cbPayload + sizeof(WCHAR);
}
return cbPayload;
}
//-----------------------------------------------------------------------------
// Function: ValidatePayloadData
// Purpose: Examines a PEER_DATA object and determines if the contents are
// displayable to the screen. NOTE: No guarantees are made about
// the format of payload data. Therefore, you should alway
// verify payload data from a PNRP endpoint before using it.
//
// Parameters:
// pEndpoint [in] : pointer to PEER_DATA object
//
// Returns: TRUE if the PEER_DATA object contains data displayable to the
// screen, FALSE otherwise
//
BOOL VerifyPayloadData(__in PPEER_DATA pData)
{
PWSTR pwzTemp = (PWSTR) pData->pbData;
ULONG cchTemp = pData->cbData / sizeof(WCHAR);
ULONG i = 0;
for (i = 0; i < cchTemp-1; i++)
{
if (!iswprint(pwzTemp[i]))
{
return FALSE;
}
}
if (pwzTemp[cchTemp-1] != L'\0')
{
return FALSE;
}
return TRUE;
}
//-------------------------------------------------
// Display Functions - present information to user
//-------------------------------------------------
//-----------------------------------------------------------------------------
// Function: DisplayPNRPEndpoint
// Purpose: Prints to screen information about a pnrp endpoint
// Parameters:
// pEndpoint [in] : pointer to PEER_PNRP_ENDPOINT_INFO object
//
HRESULT DisplayPNRPEndpoint(__in PPEER_PNRP_ENDPOINT_INFO pEndpoint)
{
HRESULT hr = S_OK;
ULONG i = 0;
WCHAR wzAddr[MAX_ADDR_LENGTH];
// Print out the endpoint's name
wprintf(L"\nPeer Name:\t%s\n", pEndpoint->pwzPeerName);
// Display associated addresses
for (i = 0; i < pEndpoint->cAddresses; i++)
{
DWORD dwLen = (sizeof(wzAddr) / sizeof(wzAddr[0]));
hr = WSAAddressToString(
(LPSOCKADDR) pEndpoint->ppAddresses[i],
sizeof(SOCKADDR_IN6), NULL, wzAddr, &dwLen);
if (SUCCEEDED(hr))
{
if (i == 0)
{
wprintf(L"Addresses:\t%s\n", wzAddr);
}
else
{
wprintf(L"\t\t%s\n", wzAddr);
}
}
}
// Display comment and info on extended payload. For the purposes
// of this sample, payload data is assumed to be a string.
if (pEndpoint->pwzComment)
{
wprintf(L"Comment:\t%s\n", pEndpoint->pwzComment);
}
if (pEndpoint->payload.cbData == 0)
{
wprintf(L"Payload:\tNo\n");
}
// Always verify payload data before using it
else
{
if (VerifyPayloadData(&pEndpoint->payload))
{
wprintf(L"Payload:\tYes (Data: %s)\n",
(PWSTR) pEndpoint->payload.pbData);
}
else
{
wprintf(L"Payload:\tYes (Data undisplayable)\n");
}
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: EnumCloudsCommand
// Purpose: Prints names of all available clouds to screen
// Parameters: None
//
// NOTE: The signature of this function must adhere to the MENU_COMMAND typedef
HRESULT EnumCloudsCommand()
{
HRESULT hr = S_OK;
ULONG cClouds;
PPEER_PNRP_CLOUD_INFO pCloudInfo;
hr = PeerPnrpGetCloudInfo(&cClouds, &pCloudInfo);
if (SUCCEEDED(hr))
{
ULONG i;
wprintf(L"Clouds:\n");
for (i = 0; i < cClouds; i++)
{
wprintf(L" %s\n", pCloudInfo[i].pwzCloudName);
}
PeerFreeData(pCloudInfo);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: PrintMenu
// Purpose: Prints a menu of options to the user
// Parameters: None
//
void PrintMenu()
{
ULONG i = 0;
PMENU_COMMAND pMenuOptions;
if (g_hRegistration != NULL)
{
pMenuOptions = g_MenuRegistered;
}
else
{
pMenuOptions = g_MenuUnregistered;
}
for(i = 0; pMenuOptions[i].pfnCommand != NULL; i++)
{
wprintf(L"\n%d. %s", i+1, pMenuOptions[i].pwzTitle);
}
wprintf(L"\n%d. %s\n> ", i+1, pMenuOptions[i].pwzTitle);
}
//-----------------------------------------------------------------------------
// Function: main
// Purpose: Program entry point
// Parameters:
// argc : count of program arguments
// argv : program arguments
//
int __cdecl main(int argc, __in_ecount(argc) char *argv[])
{
HRESULT hr = S_OK;
WCHAR wzBuff[5] = {0};
WSADATA wsaData = {0};
ULONG ulOption = 0;
ULONG cOptions = 0;
INT iWinsockResult = 0;
DWORD dwErr = 0;
PMENU_COMMAND pMenu = NULL;
BOOL fLoop = TRUE;
//Unreferenced parameters
argc;
argv;
// Setup Winsock
iWinsockResult=WSAStartup(0x0002, &wsaData);
if (iWinsockResult != 0)
{
dwErr = WSAGetLastError();
wprintf(L"Winsock error %d\n", dwErr);
return 0;
}
// Setup PNRP infrastructure
hr = PeerPnrpStartup(PNRP_VERSION);
if (FAILED(hr))
{
wprintf(L"Error starting up PNRP\nHRESULT=0x%x\n", hr);
PrintError(hr);
}
while (fLoop)
{
PrintMenu();
FLUSH_AND_GET_RESPONSE(hr, wzBuff);
ulOption = _wtoi(wzBuff) - 1;
if (g_hRegistration != NULL)
{
pMenu = g_MenuRegistered;
cOptions = celems(g_MenuRegistered);
}
else
{
pMenu = g_MenuUnregistered;
cOptions = celems(g_MenuUnregistered);
}
if (ulOption >= cOptions)
{
wprintf(L"Invalid selection.\n");
}
else if (pMenu[ulOption].pfnCommand == NULL)
{
fLoop = FALSE;
}
else
{
// Calls the function indicated by the
// selected element of the MENU_COMMAND array
hr = (*pMenu[ulOption].pfnCommand)();
if (FAILED(hr))
{
wprintf(L"Error: HRESULT=0x%x\n", hr);
PrintError(hr);
}
}
}
PeerPnrpShutdown();
WSACleanup();
}