1082 lines
31 KiB
C++
1082 lines
31 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
|
|
// Module Name:
|
|
// WcnConfigure.cpp
|
|
//
|
|
// Abstract:
|
|
// This is the program entry point were the following takes place
|
|
// Command line parsing
|
|
// Command line validation
|
|
// call the WCN functions to perform a configure or push button scenario on a WCN enabled device
|
|
|
|
#include "WcnConfigure.h"
|
|
|
|
class ATLGlobal:public CAtlExeModuleT<ATLGlobal>{} _Module;
|
|
|
|
HRESULT GetWCNDeviceInformation(__in IWCNDevice* pDevice, __out WCN_DEVICE_INFO_PARAMETERS* pWCNDeviceInformation)
|
|
{
|
|
HRESULT hr = ERROR_SUCCESS;
|
|
|
|
//A WCN device can have a variety of attributes. (These attributes generally correspond
|
|
//to TLVs in the WPS specification, although not all WPS TLVs are available as WCN attributes).
|
|
//You can use the IWCNDevice::Get*Attribute to read these attributes. Not all devices send
|
|
//all attributes -- if the device did not send a particular attribute, the Get*Attribute API
|
|
//will return HRESULT_FROM_WIN32(ERROR_NOT_FOUND).
|
|
//
|
|
//This sample demonstrates how to get the most common attributes that would be useful for
|
|
//displaying in a user interface.
|
|
|
|
|
|
//
|
|
// WCN_TYPE_DEVICE_NAME
|
|
//
|
|
|
|
//The IWCNDevice::GetStringAttribute method gets a cached attribute from the device as a string.
|
|
hr = pDevice->GetStringAttribute(
|
|
WCN_TYPE_DEVICE_NAME,
|
|
ARRAYSIZE(pWCNDeviceInformation->wszDeviceName),
|
|
pWCNDeviceInformation->wszDeviceName);
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to get the Device Name from the IWCNDevice instance. hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
wprintf(L"\nINFO: Device Name: [%s]",pWCNDeviceInformation->wszDeviceName);
|
|
|
|
|
|
//
|
|
// WCN_TYPE_MANUFACTURER
|
|
//
|
|
|
|
hr = pDevice->GetStringAttribute(
|
|
WCN_TYPE_MANUFACTURER,
|
|
ARRAYSIZE(pWCNDeviceInformation->wszManufacturerName),
|
|
pWCNDeviceInformation->wszManufacturerName);
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to get the device manufacturer from the ICWNDevice instance, hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
wprintf(L"\nINFO: Manufacturer Name: [%s]", pWCNDeviceInformation->wszManufacturerName);
|
|
|
|
|
|
//
|
|
// WCN_TYPE_MODEL_NAME
|
|
//
|
|
|
|
hr = pDevice->GetStringAttribute(
|
|
WCN_TYPE_MODEL_NAME,
|
|
ARRAYSIZE(pWCNDeviceInformation->wszModelName),
|
|
pWCNDeviceInformation->wszModelName);
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to get the device model name from the ICWNDevice instance, hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
wprintf(L"\nINFO: Model Name: [%s]", pWCNDeviceInformation->wszModelName);
|
|
|
|
|
|
//
|
|
// WCN_TYPE_MODEL_NUMBER
|
|
// Note that the Model Number is actually a string. Most devices have alpha-numeric
|
|
// model numbers, like "AB1234CD".
|
|
|
|
hr = pDevice->GetStringAttribute(
|
|
WCN_TYPE_MODEL_NUMBER,
|
|
ARRAYSIZE(pWCNDeviceInformation->wszModelNumber),
|
|
pWCNDeviceInformation->wszModelNumber);
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to get the device model name from the ICWNDevice instance, hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
wprintf(L"\nINFO: Model Number: [%s]", pWCNDeviceInformation->wszModelNumber);
|
|
|
|
|
|
//
|
|
// WCN_TYPE_SERIAL_NUMBER
|
|
// Note that the Serial Number is actually a string. Some devices send strings that
|
|
// aren't meaningful, like "(none)" or just the empty string.
|
|
|
|
hr = pDevice->GetStringAttribute(
|
|
WCN_TYPE_SERIAL_NUMBER,
|
|
ARRAYSIZE(pWCNDeviceInformation->wszSerialNumber),
|
|
pWCNDeviceInformation->wszSerialNumber);
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to get the device model name from the ICWNDevice instance, hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
wprintf(L"\nINFO: Serial Number: [%s]", pWCNDeviceInformation->wszSerialNumber);
|
|
|
|
|
|
//
|
|
// WCN_TYPE_CONFIG_METHODS
|
|
// This is a bit mask of the values from WCN_VALUE_TYPE_CONFIG_METHODS.
|
|
// For example, a devices indicates support for pushbutton if its Config
|
|
// Methods value includes the WCN_VALUE_CM_PUSHBUTTON flag.
|
|
|
|
//The GetIntegerAttribute method gets a cached attribute from the device as an integer.
|
|
hr = pDevice->GetIntegerAttribute(
|
|
WCN_TYPE_CONFIG_METHODS,
|
|
&pWCNDeviceInformation->uConfigMethods);
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to get the device model name from the ICWNDevice instance, hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT RunScenario(__in CONFIGURATION_PARAMETERS* configParams)
|
|
{
|
|
//common declarations
|
|
UINT status = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
UINT pinLen = Pin_Length_8;
|
|
|
|
//pin needs to be a null terminated ascii char[] for the IWCNDevice::SetPassword function
|
|
char pin[Pin_Length_8 + 1] = {0};
|
|
|
|
|
|
int result = 0;
|
|
|
|
//WCN declarations
|
|
CComPtr<IWCNDevice> pDevice;
|
|
CComObject<WcnConnectNotification>* pWcnConNotif = NULL;
|
|
CComObject<CWcnFdDiscoveryNotify> * wcnFdDiscoveryNotify = NULL;
|
|
|
|
//Wlan variable declarations
|
|
WCHAR profileBuffer[WCN_API_MAX_BUFFER_SIZE] = {0};
|
|
HANDLE wlanHandle = 0;
|
|
DWORD negVersion = 0;
|
|
GUID interfaceGuid = {0};
|
|
WLAN_INTERFACE_INFO_LIST* pInterfaceList = 0;
|
|
DWORD wlanResult = 0;
|
|
WLAN_CONNECTION_PARAMETERS connParams;
|
|
ZeroMemory(&connParams,sizeof(connParams));
|
|
WCN_DEVICE_INFO_PARAMETERS WCNDeviceInformation;
|
|
PWSTR pWlanProfileXml = NULL;
|
|
DWORD dwFlags = WLAN_PROFILE_GET_PLAINTEXT_KEY;
|
|
|
|
|
|
//The following wlan profile xml is used to configure an unconfigured WCN enabled Router or device.
|
|
//See http://msdn.microsoft.com/en-us/library/bb525370(VS.85).aspx on how to generate a wlan profile.
|
|
//Alternatively, you can read an existing network profile by calling WlanGetProfile.
|
|
WCHAR WCNConnectionProfileTemplate[] =
|
|
L"<?xml version=\"1.0\" ?>"
|
|
L""
|
|
L"<WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">"
|
|
L" <name>%s</name>"
|
|
L""
|
|
L" <SSIDConfig>"
|
|
L" <SSID>"
|
|
L" <name>%s</name>"
|
|
L" </SSID>"
|
|
L" </SSIDConfig>"
|
|
L" "
|
|
L" <connectionType>ESS</connectionType>"
|
|
L" <connectionMode>auto</connectionMode>"
|
|
L""
|
|
L" <MSM>"
|
|
L" <security>"
|
|
L" <authEncryption>"
|
|
L" <authentication>WPA2PSK</authentication>"
|
|
L" <encryption>AES</encryption>"
|
|
L" </authEncryption>"
|
|
L""
|
|
L""
|
|
L" <sharedKey>"
|
|
L" <keyType>passPhrase</keyType>"
|
|
L" <protected>false</protected>"
|
|
L" <keyMaterial>%s</keyMaterial>"
|
|
L" </sharedKey>"
|
|
L""
|
|
L" </security>"
|
|
L" </MSM>"
|
|
L"</WLANProfile>";
|
|
|
|
|
|
std::wstring profileXML;
|
|
|
|
//open a wlan handle - this will be used later for saving the profile to the system
|
|
status = WlanOpenHandle(
|
|
WLAN_API_VERSION_2_0,
|
|
NULL,
|
|
&negVersion,
|
|
&wlanHandle);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: WlanOpenHandle failed with the following error code [%d]", status);
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Get the first wlan device
|
|
// ideally you would want to be able to choose the wireless device you want to use
|
|
status = WlanEnumInterfaces(
|
|
wlanHandle,
|
|
NULL,
|
|
&pInterfaceList);
|
|
|
|
if(status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: WlanEnumInterfaces failed with the following error code [0x%d]",status);
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
//Make sure there is at least one wlan interface on the system
|
|
if (pInterfaceList == 0 || pInterfaceList->dwNumberOfItems == 0)
|
|
{
|
|
wprintf(L"\nERROR: No wireless network adapters on the system");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
//get the wlan interface GUID
|
|
interfaceGuid = pInterfaceList->InterfaceInfo[0].InterfaceGuid;
|
|
|
|
//Create an instance of the IWCNConnectNotify Interface
|
|
hr = CComObject<WcnConnectNotification>::CreateInstance(&pWcnConNotif);
|
|
if (hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: Creating an instance of WcnConnectNotification failed with the following error hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
pWcnConNotif->AddRef();
|
|
|
|
hr = CComObject<CWcnFdDiscoveryNotify>::CreateInstance(&wcnFdDiscoveryNotify);
|
|
if (hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: Creating an instance of CWcnFdDiscoveryNotify failed with the following error hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
wcnFdDiscoveryNotify->AddRef();
|
|
|
|
//initialize WcnConnectNotification
|
|
hr = pWcnConNotif->Init();
|
|
if(hr !=S_OK)
|
|
{
|
|
wprintf(L"\nERROR: Creating a connection notification event failed with the following error hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
//initialize CWcnFdDiscoveryNotify
|
|
hr = wcnFdDiscoveryNotify->Init(configParams->bTurnOnSoftAP);
|
|
if(hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: Initializing Function Discovery notify failed with the following error hr=[0x%x].",hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
//Search for WCN device with function discovery
|
|
hr = wcnFdDiscoveryNotify->WcnFDSearchStart(&configParams->pDeviceUUID, configParams->pSearchSSID);
|
|
if(hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: Function Discovery search failed to start with the following error hr=[0x%x].",hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
//Wait for Function Discovery to complete
|
|
wcnFdDiscoveryNotify->WaitForAnyDiscoveryEvent(Discovery_Event_Wait_Time_MS);
|
|
|
|
//Attempt to get the IWCNDevice instance
|
|
if(wcnFdDiscoveryNotify->GetWCNDeviceInstance(&pDevice))
|
|
{
|
|
//get information about the device from the IWCNDevice instance
|
|
wprintf(L"\nINFO: The following Device was found by Function Discovery.");
|
|
hr = GetWCNDeviceInformation(pDevice, &WCNDeviceInformation);
|
|
if (hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: Failed to get the Device information from the IWCNDevice Instance, hr=[0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nERROR: Device was NOT found by Function Discovery.");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
|
|
//The following segment generates a WLAN profile from the template above then saves it to the
|
|
//WLAN store. It the retrieves the profile from the WLAN store for use in configuring a router
|
|
//or device.
|
|
if (configParams->enumConfigScenario != PCConfigPin
|
|
&& configParams->enumConfigScenario != PCConfigPushButton)
|
|
{
|
|
//add the profiles ssid and passphrase to the wlan profile template
|
|
swprintf_s(
|
|
profileBuffer,
|
|
WCNConnectionProfileTemplate,
|
|
configParams->pProfileSSID,
|
|
configParams->pProfileSSID,
|
|
configParams->pProfilePassphrase);
|
|
|
|
|
|
//Add the created profile to the wlan store
|
|
status = WlanSetProfile(
|
|
wlanHandle,
|
|
&interfaceGuid,
|
|
0, //all-user profile
|
|
profileBuffer,
|
|
NULL, // Default Security - All user profile
|
|
TRUE, // Overwrite profile
|
|
NULL, // reserved
|
|
&wlanResult);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to save the profile return code was [0x%x]", wlanResult);
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: Successfully saved the profile to the wlan store");
|
|
}
|
|
|
|
//Here is where the profile is retrieved from the wlan store to be used in the configuration
|
|
//of the device.
|
|
//If so desired a list of available profiles could be presented to the user so that
|
|
//they could decied which profile will be used to configure the device
|
|
//The wlan profile must be retrieved in plain text inorder for the IWCNDEVICE::SetNetWorkProfile
|
|
// method to succeede. In order to do this you need to be elevated to get the wlan profile
|
|
// in plain text.
|
|
status = WlanGetProfile(
|
|
wlanHandle,
|
|
&interfaceGuid,
|
|
configParams->pProfileSSID,
|
|
NULL, //reserved
|
|
&pWlanProfileXml,
|
|
&dwFlags, // Flags - get profile in plain text
|
|
NULL); // GrantedAccess - none
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: WlanGetprofile Failed to get profile [%s] with error code [0x%x]", configParams->pProfileSSID, status);
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: Successfully retrieved profile [%s] from the wlan store.", configParams->pProfileSSID);
|
|
}
|
|
|
|
//check to make sure the profile from the wlan store is not a Group Policy profile
|
|
if (WLAN_PROFILE_GROUP_POLICY & dwFlags)
|
|
{
|
|
wprintf(L"\nERROR: Profile [%s] is a group policy WLAN profile which is not supported by WCN", configParams->pProfileSSID);
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//The IWCNDevice::SetNetworkProfile method queues an XML WLAN profile to be
|
|
//provisioned to the device. This method may only be called prior to IWCNDevice::Connect.
|
|
hr = pDevice->SetNetworkProfile(pWlanProfileXml);
|
|
if(hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: IWCNDevice::SetNetworkProfile failed with error code [0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: IWCNDevice::SetNetworkProfile() succeeded with result [0x%x]", hr);
|
|
}
|
|
}
|
|
|
|
switch (configParams->enumConfigScenario)
|
|
{
|
|
case DeviceConfigPushButton:
|
|
|
|
pinLen = 0;
|
|
break;
|
|
|
|
case DeviceConfigPin:
|
|
case RouterConfig:
|
|
if (configParams->pDevicePin == 0)
|
|
{
|
|
wprintf(L"\nERROR: Pin must not be 0 when doing a pin configuration");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
result = WideCharToMultiByte(
|
|
CP_UTF8,
|
|
0,
|
|
configParams->pDevicePin,
|
|
-1,
|
|
(LPSTR)pin,
|
|
sizeof(pin),
|
|
NULL,
|
|
NULL);
|
|
if (result == 0 )
|
|
{
|
|
wprintf(L"\nERROR: Failed to convert the pin to multibyte.");
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
pinLen = sizeof(pin) - 1 ;
|
|
break;
|
|
|
|
case PCConfigPushButton:
|
|
//check to make sure the device supports push button before doing the push button configuration
|
|
if (WCNDeviceInformation.uConfigMethods & WCN_VALUE_CM_PUSHBUTTON)
|
|
{
|
|
//set the pin length to 0 this is necessary for a Push button configuration scenario
|
|
pinLen = 0;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"ERROR: The [%s] device does not support the Push Button Method", WCNDeviceInformation.wszDeviceName);
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
break;
|
|
|
|
case PCConfigPin:
|
|
//check to make sure the device supports pin before doing the pin configuration
|
|
if ((WCNDeviceInformation.uConfigMethods & WCN_VALUE_CM_LABEL)||
|
|
(WCNDeviceInformation.uConfigMethods & WCN_VALUE_CM_DISPLAY))
|
|
{
|
|
if (configParams->pDevicePin == 0)
|
|
{
|
|
wprintf(L"\nERROR: Pin must not be 0 when doing a pin configuration");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
result = WideCharToMultiByte(
|
|
CP_UTF8, //CodePage
|
|
0, //Unmapped character flags
|
|
configParams->pDevicePin,
|
|
-1, //null terminated string
|
|
(LPSTR)pin,
|
|
sizeof(pin),
|
|
NULL, //lpDefaultChar - use system default value
|
|
NULL); //lpUsedDefaultChar ignored
|
|
if (result == 0 )
|
|
{
|
|
wprintf(L"\nERROR: Failed to convert the pin to multibyte.");
|
|
goto cleanup;
|
|
}
|
|
|
|
pinLen = sizeof(pin) - 1 ;
|
|
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nERROR: The [%s] device does not supprot the pin method", WCNDeviceInformation.wszDeviceName);
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//The IWCNDevice::SetPassword method configures the authentication method value, and if required,
|
|
//a password used for the pending session. This method may only be called prior to IWCNDevice::Connect.
|
|
hr = pDevice->SetPassword(
|
|
configParams->enumConfigType,
|
|
pinLen,
|
|
(BYTE*)pin);
|
|
|
|
if(hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: IWCNDevice::SetPassword failed with error code [0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: IWCNDevice::SetPassword succeeded with result [0x%x]", hr);
|
|
}
|
|
|
|
|
|
//The IWCNDevice::Connect method initiates the session.
|
|
hr = pDevice->Connect(pWcnConNotif);
|
|
if(hr != S_OK)
|
|
{
|
|
//Device Push button configuration is only supported on SoftAP capable wireless Nics
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_CONNECTION_UNAVAIL)
|
|
&& configParams->enumConfigScenario == DeviceConfigPushButton)
|
|
{
|
|
wprintf(L"\nERROR: PushButton Configuration of non AP devices is only supported on");
|
|
wprintf(L"\n SoftAP capable wireless network cards.");
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nERROR: IWCNDevice::Connect failed with error code [0x%x]", hr);
|
|
}
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: IWCNDevice::Connect succeeded with result [0x%x]", hr);
|
|
}
|
|
|
|
//wait for the configuration result
|
|
hr = pWcnConNotif->WaitForConnectionResult();
|
|
if (hr != S_OK)
|
|
{
|
|
wprintf(L"ERROR: WaitforconnectionResult returned the following error [ox%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
//check to see which connection callbacks were called
|
|
if(pWcnConNotif->connectSucceededCallBackInvoked)
|
|
{
|
|
wprintf(L"\nINFO: IWCNConnectNotify::ConnectSucceeded was invoked");
|
|
}
|
|
else if(pWcnConNotif->connectFailedCallBackInvoked)
|
|
{
|
|
wprintf(L"\nERROR: IWCNConnectNotify::ConnectFailed was invoked");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//save the profile from the IWCNDevice instance to the WLAN store if doing a PCConfigPushButton
|
|
//or a PCConfigPin scenario
|
|
|
|
// this is the profile that was received from the router
|
|
if (configParams->enumConfigScenario == PCConfigPushButton || configParams->enumConfigScenario == PCConfigPin)
|
|
{
|
|
//The IWCNDevice::GetNetworkProfile method gets a network profile from the device.
|
|
hr = pDevice->GetNetworkProfile(ARRAYSIZE(profileBuffer), profileBuffer);
|
|
if(hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: IWCNDevice::GetNetworkProfile failed with [0x%x]", hr);
|
|
goto cleanup;
|
|
}
|
|
|
|
//save the profile to the system if doing a RouterConfig or a pushbutton scenario
|
|
//The SoftapConfig and DeviceConfig scenarios will generally use a profile that is already on the system
|
|
//save the profile to the wlan interface
|
|
status = WlanSetProfile(
|
|
wlanHandle,
|
|
&interfaceGuid,
|
|
0, //Flags - none
|
|
profileBuffer,
|
|
NULL, // Default Security - All user profile
|
|
TRUE, // Overwrite profile
|
|
NULL, // reserved
|
|
&wlanResult);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to save the profile to the WLAN store, return code was [0x%x]", wlanResult);
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: Successfully saved the profile to the WLAN store");
|
|
}
|
|
}
|
|
|
|
//Display the SSID and passphrase used to configure the Router or device
|
|
if (configParams->enumConfigScenario != PCConfigPin && configParams->enumConfigScenario != PCConfigPushButton)
|
|
{
|
|
wprintf(L"\nINFO: Profile SSID Used: [%s]", configParams->pProfileSSID);
|
|
wprintf(L"\nINFO: Profile Passphrase Used: [%s]", configParams->pProfilePassphrase);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if(pWcnConNotif)
|
|
{
|
|
pWcnConNotif->Release();
|
|
pWcnConNotif = 0;
|
|
}
|
|
|
|
if(wcnFdDiscoveryNotify)
|
|
{
|
|
wcnFdDiscoveryNotify->Release();
|
|
wcnFdDiscoveryNotify = 0;
|
|
}
|
|
|
|
if (wlanHandle != NULL)
|
|
{
|
|
WlanCloseHandle(wlanHandle,NULL);
|
|
}
|
|
|
|
if (pInterfaceList != NULL)
|
|
{
|
|
WlanFreeMemory(pInterfaceList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void printUsage()
|
|
{
|
|
wprintf(L"\nUSAGE:");
|
|
wprintf(L"\n WCNConfigure.exe");
|
|
wprintf(L"\n Scenario=[DeviceConfigPin | DeviceConfigPushButton | RouterConfig |");
|
|
wprintf(L"\n PCConfigPushButton | PCConfigPin ]");
|
|
wprintf(L"\n [UUID=<uuid of device> | SEARCHSSID=<ssid of device to find>]");
|
|
wprintf(L"\n [PIN=<pin of device>]");
|
|
wprintf(L"\n [PROFILESSID=<ssid to use in profile>]");
|
|
wprintf(L"\n [PROFILEPASSPHRASE=<passphrase to use in profile>]");
|
|
wprintf(L"\n");
|
|
wprintf(L"\nParameters:");
|
|
wprintf(L"\n Scenario - choose the operation you wish to perform ");
|
|
wprintf(L"\n DeviceConfigPushButton - Configure a WCN enabled device, such as a picture");
|
|
wprintf(L"\n frame using the button on the device");
|
|
wprintf(L"\n DeviceConfigPin - Configure a WCN enabled device, such as a picture frame");
|
|
wprintf(L"\n using the device supplied pin");
|
|
wprintf(L"\n RouterConfig - Configure a WCN enabled Wireless Router");
|
|
wprintf(L"\n PCConfigPushButton - Get the wireless profile from a WCN enabled router");
|
|
wprintf(L"\n using the Push Button on the device.");
|
|
wprintf(L"\n PCConfigPin - Get the wireless profile from a WCN enabled rotuer using the");
|
|
wprintf(L"\n supplied pin.");
|
|
wprintf(L"\n");
|
|
wprintf(L"\n UUID - Enter a device UUID in the following format xxxx-xxxx-xxxx-xxxxxxxxxxxx");
|
|
wprintf(L"\n UUID is necessary for the DeviceConfigPushButton and DeviceConfigPin");
|
|
wprintf(L"\n scenarios. Use either UUID or SEARCHSSID for the RouterConfig, PCConfigPin");
|
|
wprintf(L"\n and PCConfigPushButton scenarios.");
|
|
wprintf(L"\n");
|
|
wprintf(L"\n SEARCHSSID - Enter in the SSID for the Router you are looking to configure.");
|
|
wprintf(L"\n SEARCHSSID is only valid in the RouterConfig, PCConfigPushButton and ");
|
|
wprintf(L"\n PCConfigPin scenarios. Use either UUID or SEARCHSSID for the these");
|
|
wprintf(L"\n scenarios. NOTE: Using SSID will return the first device");
|
|
wprintf(L"\n found with that ssid. If there is more than one device with the");
|
|
wprintf(L"\n same ssid use the UUID instead");
|
|
wprintf(L"\n");
|
|
wprintf(L"\n PIN - Enter the pin of the device");
|
|
wprintf(L"\n PIN is only valid when using the RouterConfig and DeviceConfigPIN");
|
|
wprintf(L"\n Scenarios.");
|
|
wprintf(L"\n");
|
|
wprintf(L"\n PROFILESSID - When present this SSID will be used in the WLAN profile that is");
|
|
wprintf(L"\n pushed to the router/device otherwise a default SSID of WCNSSID ");
|
|
wprintf(L"\n will be used");
|
|
wprintf(L"\n");
|
|
wprintf(L"\n PROFILEPASSPHRASE - when present this passphrase will be used in the wlan");
|
|
wprintf(L"\n profile that is pushed to the router/device. Otherwise, a");
|
|
wprintf(L"\n random default passphrase will be used\n\n");
|
|
}
|
|
|
|
BOOL validateParameters(__in CONFIGURATION_PARAMETERS* configParams)
|
|
{
|
|
BOOL bReturnValue = FALSE;
|
|
|
|
switch (configParams->enumConfigScenario)
|
|
{
|
|
//DeviceConfig and RouterConfig require both the uuid and the device pin
|
|
case DeviceConfigPin:
|
|
if (configParams->pDeviceUUID != GUID_NULL && configParams->pDevicePin != 0)
|
|
{
|
|
bReturnValue = TRUE;
|
|
}
|
|
break;
|
|
|
|
case DeviceConfigPushButton:
|
|
if (configParams->pDeviceUUID != GUID_NULL)
|
|
{
|
|
bReturnValue = TRUE;
|
|
}
|
|
break;
|
|
|
|
case RouterConfig:
|
|
//uuid or searchssid must be present in order to continue
|
|
if ((configParams->pDeviceUUID != GUID_NULL || configParams->pSearchSSID != 0)
|
|
&& configParams->pDevicePin != 0)
|
|
{
|
|
bReturnValue = TRUE;
|
|
}
|
|
break;
|
|
|
|
case PCConfigPushButton:
|
|
if (configParams->pDeviceUUID != GUID_NULL || configParams->pSearchSSID != 0)
|
|
{
|
|
bReturnValue = TRUE;
|
|
}
|
|
break;
|
|
|
|
case PCConfigPin:
|
|
if ((configParams->pDeviceUUID != GUID_NULL || configParams->pSearchSSID != 0)
|
|
&& configParams->pDevicePin != 0)
|
|
{
|
|
bReturnValue = TRUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
HRESULT parseCommandLineArguments(__out CONFIGURATION_PARAMETERS* configParameters, __in DWORD argc, __in_ecount(argc) WCHAR **pArg)
|
|
{
|
|
HRESULT hr = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
//parse the command line inputs
|
|
for(DWORD i = 1;i < argc;i++)
|
|
{
|
|
wchar_t* pCommand = 0;
|
|
wchar_t* pContext = 0;
|
|
|
|
pCommand = wcstok_s(
|
|
pArg[i],
|
|
L"=",
|
|
&pContext);
|
|
|
|
if(_wcsicmp(pCommand,L"Scenario") == 0)
|
|
{
|
|
if (pContext)
|
|
{
|
|
if ( _wcsicmp(pContext, L"RouterConfig") == 0)
|
|
{
|
|
configParameters->enumConfigScenario = RouterConfig;
|
|
configParameters->enumConfigType = WCN_PASSWORD_TYPE_PIN;
|
|
}
|
|
else if ( _wcsicmp(pContext, L"DeviceConfigPushButton") == 0)
|
|
{
|
|
configParameters->enumConfigScenario = DeviceConfigPushButton;
|
|
configParameters->enumConfigType = WCN_PASSWORD_TYPE_PUSH_BUTTON;
|
|
configParameters->bTurnOnSoftAP = TRUE;
|
|
}
|
|
else if ( _wcsicmp(pContext, L"DeviceConfigPin") == 0)
|
|
{
|
|
configParameters->enumConfigScenario = DeviceConfigPin;
|
|
configParameters->enumConfigType = WCN_PASSWORD_TYPE_PIN;
|
|
configParameters->bTurnOnSoftAP = TRUE;
|
|
}
|
|
else if (_wcsicmp(pContext, L"PCConfigPushButton") == 0)
|
|
{
|
|
configParameters->enumConfigScenario = PCConfigPushButton;
|
|
configParameters->enumConfigType = WCN_PASSWORD_TYPE_PUSH_BUTTON;
|
|
}
|
|
else if (_wcsicmp(pContext, L"PCConfigPin") == 0)
|
|
{
|
|
configParameters->enumConfigScenario = PCConfigPin;
|
|
configParameters->enumConfigType = WCN_PASSWORD_TYPE_PIN;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nERROR: The supplied option for Scenairo is not valid\n\n");
|
|
hr = S_FALSE;
|
|
printUsage();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(_wcsicmp(pCommand,L"UUID") == 0)
|
|
{
|
|
if (pContext)
|
|
{
|
|
if (wcslen(pContext) == UUID_LENGTH)
|
|
{
|
|
hr = UuidFromString((RPC_WSTR)pContext, &configParameters->pDeviceUUID);
|
|
if (hr != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Failed to convert supplied uuid: HR=[0x%x]\n\n",hr);
|
|
printUsage();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nERROR: The supplied UUID is not valid\n\n");
|
|
hr = S_FALSE;
|
|
printUsage();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(pCommand,L"PIN") == 0)
|
|
{
|
|
//valid pin lengths are 4 and 8
|
|
if (pContext)
|
|
{
|
|
if (wcslen(pContext) == Pin_Length_8 || wcslen(pContext) == Pin_Length_4)
|
|
{
|
|
configParameters->pDevicePin = pContext;
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nERROR: The supplied PIN is not valid\n\n");
|
|
hr = S_FALSE;
|
|
printUsage();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(pCommand, L"SEARCHSSID") == 0)
|
|
{
|
|
if (pContext)
|
|
{
|
|
if (wcslen(pContext) > DOT11_SSID_MAX_LENGTH)
|
|
{
|
|
wprintf(L"\nERROR: Search SSID length is too long");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
configParameters->pSearchSSID = pContext;
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(pCommand, L"ProfileSSID") == 0)
|
|
{
|
|
if (pContext)
|
|
{
|
|
if (wcslen(pContext) > DOT11_SSID_MAX_LENGTH + 1)
|
|
{
|
|
wprintf(L"\nERROR: profile SSID length is too long");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
configParameters->pProfileSSID = pContext;
|
|
}
|
|
}
|
|
|
|
if (_wcsicmp(pCommand, L"ProfilePassphrase") == 0)
|
|
{
|
|
if (pContext)
|
|
{
|
|
if (wcslen(pContext) < PASSPHRASE_MIN_LENGTH || wcslen(pContext) > PASSPHRASE_MAX_LENGTH)
|
|
{
|
|
wprintf(L"\nERROR: Passphrase must be between 8 and 63 characters long");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
configParameters->pProfilePassphrase = pContext;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if the ProfileSSID is not present add one
|
|
if (configParameters->pProfileSSID == 0)
|
|
{
|
|
configParameters->pProfileSSID = L"WCNSSID";
|
|
}
|
|
|
|
//if ProfilePassphrase is not supplied generate one based on a random number
|
|
if (configParameters->pProfilePassphrase == 0)
|
|
{
|
|
|
|
WCHAR tempPassphrase[dwCharsToGenerate + 1] = { };
|
|
for (int i = 0; i < dwCharsToGenerate - 1; ++i)
|
|
{
|
|
if(i == 4 || i== 9) //Apply dash separator for the passphrase
|
|
{
|
|
tempPassphrase[i] = '-';
|
|
}
|
|
else
|
|
{
|
|
unsigned int r;
|
|
errno_t err = rand_s(&r);
|
|
|
|
if (err != 0)
|
|
{
|
|
hr = S_FALSE;
|
|
wprintf(L"\nERROR: Failed to generate a random number.");
|
|
goto cleanup;
|
|
}
|
|
|
|
tempPassphrase[i] = PassphraseCharacterSet[r % 65];
|
|
|
|
}
|
|
}
|
|
tempPassphrase[dwCharsToGenerate] = L'\0';
|
|
|
|
configParameters->pProfilePassphrase = _wcsdup(tempPassphrase);
|
|
configParameters->bFreePassphrase = TRUE;
|
|
}
|
|
|
|
//validate the command line parameters
|
|
if (!validateParameters(configParameters))
|
|
{
|
|
printUsage();
|
|
wprintf(L"\nERROR: Missing Parameter");
|
|
hr = S_FALSE;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
int wmain(__in DWORD argc, __in_ecount(argc) WCHAR **pArg)
|
|
{
|
|
HRESULT hr = ERROR_SUCCESS;
|
|
UINT status = ERROR_SUCCESS;
|
|
BOOL fUnintializeCom = FALSE;
|
|
CONFIGURATION_PARAMETERS configParameters;
|
|
DWORD dwVersion = 0;
|
|
DWORD dwMajorVersion = 0;
|
|
DWORD dwMinorVersion= 0;
|
|
|
|
//check to make sure we are running on Windows 7 or later
|
|
dwVersion = GetVersion();
|
|
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
|
|
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
|
|
|
|
configParameters.enumConfigScenario = None;
|
|
configParameters.pDeviceUUID = GUID_NULL;
|
|
configParameters.pDevicePin = 0;
|
|
configParameters.pSearchSSID = 0;
|
|
configParameters.bTurnOnSoftAP = FALSE;
|
|
configParameters.pProfilePassphrase = 0;
|
|
configParameters.pProfileSSID = 0;
|
|
configParameters.bFreePassphrase = FALSE;
|
|
|
|
|
|
//dwMajorVersion must be 6 or greater and dwMinorVersion must be 1 or greater (Vista is 6.0)
|
|
if (dwMajorVersion <= WINDOWS7_MAJOR_VERSION)
|
|
{
|
|
if ((dwMajorVersion == WINDOWS7_MAJOR_VERSION && dwMinorVersion < WINDOWS7_MINOR_VERSION)
|
|
|| dwMajorVersion < WINDOWS7_MAJOR_VERSION)
|
|
{
|
|
wprintf(L"\nERROR: This Application requires Windows 7 or later\n\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//initialize Com
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED|COINIT_DISABLE_OLE1DDE);
|
|
if (hr != S_OK && hr != S_FALSE)
|
|
{
|
|
wprintf(L"\nERROR: Com failed to initialize with the following error [0x%x]\n\n", hr);
|
|
goto cleanup;
|
|
}
|
|
fUnintializeCom = TRUE;
|
|
|
|
//get the parameters from the command line
|
|
hr = parseCommandLineArguments(&configParameters, argc, pArg);
|
|
if (hr != S_OK)
|
|
{
|
|
wprintf(L"\nERROR: failed to parse command line arguments\n\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
//select the scenario you wish to run
|
|
switch (configParameters.enumConfigScenario)
|
|
{
|
|
//configure a wireless device using the device supplied pin code
|
|
case DeviceConfigPin:
|
|
status = RunScenario(&configParameters);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Configuration of the wireless Device with a PIN Failed\n\n");
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: Configuration of the wireless Device with a PIN Succeeded\n\n");
|
|
}
|
|
break;
|
|
|
|
//configure a wireless device with by pushing the configuration button on the device
|
|
case DeviceConfigPushButton:
|
|
status = RunScenario(&configParameters);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Configuration of the Wireless device with push button Failed\n\n");
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: Configuration of the Wireless device with push button Succeeded\n\n");
|
|
}
|
|
break;
|
|
|
|
//configure a router using the router supplied pin code
|
|
case RouterConfig:
|
|
status = RunScenario(&configParameters);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: Configuration of the Wireless Router Failed\n\n");
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: Configuration of the Wireless Router Succeeded\n\n");
|
|
}
|
|
break;
|
|
|
|
// get the wireless profile from the router using the configuration button on the router
|
|
case PCConfigPushButton:
|
|
wprintf(L"\n\nINFO: Please push the 'WCN Configure Button' on the router and then hit enter.\n");
|
|
if (getchar() == '\n')
|
|
{
|
|
wprintf(L"\nINFO: Attempting to get the Wireless profile from the router");
|
|
}
|
|
|
|
status = RunScenario(&configParameters);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: PC Configuration with the Push Button failed\n\n");
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: PC Configuration with the Push Button succeeded\n\n");
|
|
}
|
|
break;
|
|
|
|
case PCConfigPin:
|
|
status = RunScenario(&configParameters);
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
wprintf(L"\nERROR: PC Configuration with a pin Failed\n\n");
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"\nINFO: PC Configuration with a pin Succeeded\n\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
cleanup:
|
|
|
|
if(configParameters.bFreePassphrase)
|
|
{
|
|
free(configParameters.pProfilePassphrase);
|
|
}
|
|
|
|
if (fUnintializeCom)
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|