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

1121 lines
25 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 "stdafx.h"
//
// WLAN notification callback
//
VOID WINAPI
CWlanManager::WlanNotificationCallback(
PWLAN_NOTIFICATION_DATA pNotifData,
PVOID pContext
)
{
CWlanManager* pWlanManager = (CWlanManager *)pContext;
pWlanManager->Notify(pNotifData);
}
// CWlanManager
CWlanManager::CWlanManager() :
m_WlanHandle(NULL),
m_NotificationSink(NULL),
m_CallbackComplete(NULL),
m_Initialized(false)
{
// initialize critical section first, throw exception
InitializeCriticalSection(&m_CriticalSection);
}
CWlanManager::~CWlanManager()
{
// Unadvise, ignore the error
UnadviseHostedNetworkNotification();
if (m_WlanHandle != NULL)
{
WlanCloseHandle(m_WlanHandle, NULL);
m_WlanHandle = NULL;
}
if (m_CallbackComplete != NULL)
{
CloseHandle(m_CallbackComplete);
m_CallbackComplete = NULL;
}
DeleteCriticalSection(&m_CriticalSection);
}
HRESULT
CWlanManager::Init()
{
HRESULT hr = S_OK;
DWORD retCode = ERROR_SUCCESS;
DWORD dwDataSize = 0;
BOOL *pbMode = NULL; // whether hosted network is allowed or not
PWLAN_HOSTED_NETWORK_CONNECTION_SETTINGS pConnSettings = NULL; // hosted network connectivity settings
PWLAN_HOSTED_NETWORK_SECURITY_SETTINGS pSecSettings = NULL; // hosted network security settings
PWLAN_HOSTED_NETWORK_STATUS pAPStatus = NULL; // hosted network status
WLAN_OPCODE_VALUE_TYPE valueType;
Lock();
if (m_Initialized)
{
//
// no-op because it is already initialized
//
BAIL();
}
// open a wlan handle first
retCode = WlanOpenHandle(
WLAN_API_VERSION,
NULL, // reserved
&m_ServerVersion,
&m_WlanHandle
);
BAIL_ON_WIN32_ERROR(retCode, hr);
// register notifications
retCode = WlanRegisterNotification(
m_WlanHandle,
WLAN_NOTIFICATION_SOURCE_HNWK,
TRUE,
&CWlanManager::WlanNotificationCallback,
this,
NULL, // reserved
NULL
);
BAIL_ON_WIN32_ERROR(retCode, hr);
//
// Initialize the hosted network.
// It is a no-op if the hosted network is already initialized.
// Bail out if it fails.
//
retCode = WlanHostedNetworkInitSettings(
m_WlanHandle,
NULL,
NULL // reserved
);
BAIL_ON_WIN32_ERROR(retCode, hr);
//
// Is hosted network enabled?
//
retCode = WlanHostedNetworkQueryProperty(
m_WlanHandle,
wlan_hosted_network_opcode_enable,
&dwDataSize,
(PVOID *)&pbMode,
&valueType,
NULL // reserved
);
BAIL_ON_WIN32_ERROR(retCode, hr);
if(!pbMode || dwDataSize < sizeof(BOOL))
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_DATA, hr);
}
//
// get the hosted network connectivity settings
//
retCode = WlanHostedNetworkQueryProperty(
m_WlanHandle,
wlan_hosted_network_opcode_connection_settings,
&dwDataSize,
(PVOID *)&pConnSettings,
&valueType,
NULL // reserved
);
BAIL_ON_WIN32_ERROR(retCode, hr);
if( !pConnSettings || dwDataSize < sizeof(WLAN_HOSTED_NETWORK_CONNECTION_SETTINGS))
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_DATA, hr);
}
//
// get the hosted network seucrity settings
//
retCode = WlanHostedNetworkQueryProperty(
m_WlanHandle,
wlan_hosted_network_opcode_security_settings,
&dwDataSize,
(PVOID *)&pSecSettings,
&valueType,
NULL // reserved
);
BAIL_ON_WIN32_ERROR(retCode, hr);
if( !pSecSettings || dwDataSize < sizeof(WLAN_HOSTED_NETWORK_SECURITY_SETTINGS))
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_DATA, hr);
}
//
// get the hosted network status
//
retCode = WlanHostedNetworkQueryStatus(
m_WlanHandle,
&pAPStatus,
NULL // reserved
);
BAIL_ON_WIN32_ERROR(retCode, hr);
//
// save the values
//
m_HostedNetworkAllowed = *pbMode;
m_HostedNetworkConnSettings = *pConnSettings;
m_HostedNetworkSecSettings = *pSecSettings;
m_HostedNetworkState = pAPStatus->HostedNetworkState;
//
// add existing stations if the hosted network has started already
//
if (wlan_hosted_network_active == pAPStatus->HostedNetworkState)
{
for (DWORD i = 0; i < pAPStatus->dwNumberOfPeers; i++)
{
OnStationJoin(pAPStatus->PeerList[i]);
}
}
m_Initialized = true;
error:
if (retCode != ERROR_SUCCESS && m_WlanHandle != NULL)
{
//
// Close WLAN handle in failure cases
//
WlanCloseHandle(m_WlanHandle, NULL);
m_WlanHandle = NULL;
}
Unlock();
if (pbMode != NULL)
{
WlanFreeMemory(pbMode);
pbMode = NULL;
}
if (pConnSettings != NULL)
{
WlanFreeMemory(pConnSettings);
pConnSettings = NULL;
}
if (pSecSettings != NULL)
{
WlanFreeMemory(pSecSettings);
pSecSettings = NULL;
}
if (pAPStatus != NULL)
{
WlanFreeMemory(pAPStatus);
pAPStatus = NULL;
}
return hr;
}
// process the notifications received from hosted network
VOID
CWlanManager::Notify(
const PWLAN_NOTIFICATION_DATA pNotifData
)
{
if (pNotifData != NULL && WLAN_NOTIFICATION_SOURCE_HNWK == pNotifData->NotificationSource)
{
//
// Only look at hosted network notifications
//
switch (pNotifData->NotificationCode)
{
case wlan_hosted_network_state_change:
if (sizeof(WLAN_HOSTED_NETWORK_STATE_CHANGE) == pNotifData->dwDataSize && pNotifData->pData != NULL)
{
PWLAN_HOSTED_NETWORK_STATE_CHANGE pStateChange = (PWLAN_HOSTED_NETWORK_STATE_CHANGE)pNotifData->pData;
switch (pStateChange->NewState)
{
case wlan_hosted_network_active:
OnHostedNetworkStarted();
break;
case wlan_hosted_network_idle:
if (wlan_hosted_network_active == pStateChange->OldState)
{
OnHostedNetworkStopped();
}
else
{
OnHostedNetworkAvailable();
}
break;
case wlan_hosted_network_unavailable:
if (wlan_hosted_network_active == pStateChange->OldState)
{
OnHostedNetworkStopped();
}
OnHostedNetworkNotAvailable();
break;
}
}
else
{
//
// Shall NOT happen
//
_ASSERT(FALSE);
}
break;
case wlan_hosted_network_peer_state_change:
if (sizeof(WLAN_HOSTED_NETWORK_DATA_PEER_STATE_CHANGE) == pNotifData->dwDataSize && pNotifData->pData != NULL)
{
PWLAN_HOSTED_NETWORK_DATA_PEER_STATE_CHANGE pPeerStateChange = (PWLAN_HOSTED_NETWORK_DATA_PEER_STATE_CHANGE)pNotifData->pData;
if (wlan_hosted_network_peer_state_authenticated == pPeerStateChange->NewState.PeerAuthState)
{
//
// A station joined the hosted network
//
OnStationJoin(pPeerStateChange->NewState);
}
else if (wlan_hosted_network_peer_state_invalid == pPeerStateChange->NewState.PeerAuthState)
{
//
// A station left the hosted network
//
OnStationLeave(pPeerStateChange->NewState.PeerMacAddress);
}
else
{
//
// The authentication state changed
//
OnStationStateChange(pPeerStateChange->NewState);
}
}
else
{
//
// Shall NOT happen
//
_ASSERT(FALSE);
}
break;
case wlan_hosted_network_radio_state_change:
if (sizeof(WLAN_HOSTED_NETWORK_RADIO_STATE) == pNotifData->dwDataSize && pNotifData->pData != NULL)
{
// PWLAN_HOSTED_NETWORK_RADIO_STATE pRadioState = (PWLAN_HOSTED_NETWORK_RADIO_STATE)pNotifData->pData;
//
// Do nothing for now
//
}
else
{
//
// Shall NOT happen
//
_ASSERT(FALSE);
}
break;
}
}
}
VOID
CWlanManager::OnHostedNetworkStarted()
{
CHostedNetworkNotificationSink * pSink = NULL;
Lock();
//
// Change hosted network state
//
m_HostedNetworkState = wlan_hosted_network_active;
pSink = GetNotificationSink();
Unlock();
if (pSink != NULL)
{
//
// Notify client
//
pSink->OnHostedNetworkStarted();
_ASSERT(m_CallbackComplete != NULL);
//
// Signal callback complete
//
SetEvent(m_CallbackComplete);
}
}
VOID
CWlanManager::OnHostedNetworkStopped()
{
CHostedNetworkNotificationSink * pSink = NULL;
CRefObjList<CWlanStation *> stationList;
Lock();
// Save all stations that haven't left the network
while ( 0 != m_StationList.GetCount() )
{
CWlanStation* pStation = m_StationList.RemoveHead();
stationList.AddTail(pStation);
}
m_HostedNetworkState = wlan_hosted_network_idle;
pSink = GetNotificationSink();
Unlock();
if (pSink != NULL)
{
//
// Notify client
//
// notify station leave
while ( 0 != stationList.GetCount() )
{
CWlanStation* pStation = stationList.RemoveHead();
pSink->OnStationLeave(pStation);
pStation->Release();
pStation = NULL;
}
pSink->OnHostedNetworkStopped();
_ASSERT(m_CallbackComplete != NULL);
//
// Signal callback complete
//
SetEvent(m_CallbackComplete);
}
}
VOID
CWlanManager::OnHostedNetworkNotAvailable()
{
CHostedNetworkNotificationSink * pSink = NULL;
Lock();
//
// Change hosted network state
//
m_HostedNetworkState = wlan_hosted_network_unavailable;
pSink = GetNotificationSink();
Unlock();
if (pSink != NULL)
{
//
// Notify client
//
pSink->OnHostedNetworkNotAvailable();
_ASSERT(m_CallbackComplete != NULL);
//
// Signal callback complete
//
SetEvent(m_CallbackComplete);
}
}
VOID
CWlanManager::OnHostedNetworkAvailable()
{
CHostedNetworkNotificationSink * pSink = NULL;
Lock();
//
// Change hosted network state
//
m_HostedNetworkState = wlan_hosted_network_idle;
pSink = GetNotificationSink();
Unlock();
if (pSink != NULL)
{
//
// Notify client
//
pSink->OnHostedNetworkAvailable();
_ASSERT(m_CallbackComplete != NULL);
//
// Signal callback complete
//
SetEvent(m_CallbackComplete);
}
}
VOID
CWlanManager::OnStationJoin(
const WLAN_HOSTED_NETWORK_PEER_STATE& StationState
)
{
CHostedNetworkNotificationSink * pSink = NULL;
CWlanStation * pStation = new(std::nothrow) CWlanStation(StationState);
if (pStation != NULL)
{
Lock();
//
// The station should not be in the station list
//
_ASSERT(!m_StationList.IsInArray(pStation));
pStation->AddRef();
m_StationList.AddTail(pStation);
pSink = GetNotificationSink();
Unlock();
if (pSink != NULL)
{
//
// Notify client
//
pSink->OnStationJoin(pStation);
_ASSERT(m_CallbackComplete != NULL);
//
// Signal callback complete
//
SetEvent(m_CallbackComplete);
}
pStation->Release();
pStation = NULL;
}
}
VOID
CWlanManager::OnStationLeave(
DOT11_MAC_ADDRESS MacAddress
)
{
CHostedNetworkNotificationSink * pSink = NULL;
CWlanStation * pStation = NULL;
Lock();
//
// Find the station and remove it from the station list
//
for (size_t i = 0; i < m_StationList.GetCount(); i++)
{
POSITION pos = m_StationList.FindIndex(i);
CWlanStation* pTmpStation = m_StationList.GetAt(pos);
if (*pTmpStation == MacAddress)
{
//
// Found the station, remove it from the list
//
m_StationList.RemoveAt(pos);
pStation = pTmpStation;
break;
}
}
//
// The station should be in the station list
//
_ASSERT(pStation != NULL);
if (pStation != NULL)
{
pSink = GetNotificationSink();
}
Unlock();
if (pSink != NULL)
{
//
// Notify client
//
pSink->OnStationLeave(pStation);
_ASSERT(m_CallbackComplete != NULL);
//
// Signal callback complete
//
SetEvent(m_CallbackComplete);
}
if (pStation != NULL)
{
pStation->Release();
pStation = NULL;
}
}
VOID
CWlanManager::OnStationStateChange(
const WLAN_HOSTED_NETWORK_PEER_STATE&
)
{
//
// It shall NOT happen for now
//
_ASSERT(FALSE);
}
HRESULT
CWlanManager::SetHostedNetworkName(
CAtlString& strSsid
)
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
DOT11_SSID ssid, oldSsid;
//
// Convert string to SSID
//
dwError = StringToSsid(strSsid, &ssid);
BAIL_ON_WIN32_ERROR(dwError, hr);
Lock();
if (m_Initialized)
{
// save old SSID
oldSsid = m_HostedNetworkConnSettings.hostedNetworkSSID;
m_HostedNetworkConnSettings.hostedNetworkSSID = ssid;
// set the new connection setttings
dwError = WlanHostedNetworkSetProperty(
m_WlanHandle,
wlan_hosted_network_opcode_connection_settings,
sizeof( WLAN_HOSTED_NETWORK_CONNECTION_SETTINGS),
(PVOID)&m_HostedNetworkConnSettings,
NULL,
NULL
);
if (dwError != ERROR_SUCCESS)
{
// revert back to old SSID
m_HostedNetworkConnSettings.hostedNetworkSSID = oldSsid;
hr = HRESULT_FROM_WIN32(dwError);
}
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
}
Unlock();
error:
return hr;
}
HRESULT
CWlanManager::GetHostedNetworkName(
CAtlString& strSsid
)
{
DWORD dwError = ERROR_SUCCESS;
WCHAR wszSsid[WLAN_MAX_NAME_LENGTH];
DWORD dwSsidStrLen = WLAN_MAX_NAME_LENGTH;
Lock();
wszSsid[0] = L'\0';
if (m_Initialized)
{
//
// Convert SSID to string
//
dwError = SsidToDisplayName(
&m_HostedNetworkConnSettings.hostedNetworkSSID,
TRUE,
wszSsid,
&dwSsidStrLen
);
if (ERROR_SUCCESS == dwError)
{
strSsid = wszSsid;
}
}
else
{
dwError = ERROR_INVALID_STATE;
}
Unlock();
return HRESULT_FROM_WIN32(dwError);
}
HRESULT
CWlanManager::SetHostedNetworkKey(
CAtlString& strKey
)
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
UCHAR strKeyBuf[WLAN_MAX_NAME_LENGTH];
DWORD dwKeyBufLen = WLAN_MAX_NAME_LENGTH;
dwError = ConvertPassPhraseKeyStringToBuffer(
strKey,
strKey.GetLength(),
DOT11_AUTH_ALGO_RSNA_PSK,
strKeyBuf,
&dwKeyBufLen
);
BAIL_ON_WIN32_ERROR(dwError, hr);
if (0 == dwKeyBufLen)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_PARAMETER, hr);
}
dwError = WlanHostedNetworkSetSecondaryKey(
m_WlanHandle,
dwKeyBufLen,
strKeyBuf,
TRUE, // passphrase
TRUE, // persistent
NULL, // not interested in failure reason
NULL // reserved
);
BAIL_ON_WIN32_ERROR(dwError, hr);
error:
return hr;
}
HRESULT
CWlanManager::GetHostedNetworkKey(
CAtlString& strKey
)
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
BOOL bIsPassPhrase = FALSE;
BOOL bPersistent = FALSE;
PUCHAR pucSecondaryKey = NULL;
DWORD dwSecondaryKeyLength = 0;
WCHAR strSecondaryKey[WLAN_MAX_NAME_LENGTH];
// get the user security key
dwError = WlanHostedNetworkQuerySecondaryKey(
m_WlanHandle,
&dwSecondaryKeyLength,
&pucSecondaryKey,
&bIsPassPhrase,
&bPersistent,
NULL,
NULL
);
BAIL_ON_WIN32_ERROR(dwError, hr);
int cchKey = 0;
if (dwSecondaryKeyLength > 0)
{
// Must be passphrase
_ASSERT(bIsPassPhrase);
// convert the key
if (bIsPassPhrase)
{
#pragma prefast(suppress:26035, "If the key is a pass phrase, it is guaranteed to be null-terminated.")
cchKey = MultiByteToWideChar(
CP_ACP,
MB_ERR_INVALID_CHARS,
(LPCSTR)pucSecondaryKey,
dwSecondaryKeyLength,
strSecondaryKey,
sizeof(strSecondaryKey) / sizeof(strSecondaryKey[0]) -1
);
}
}
if(cchKey == 0)
{
// secondary key is not set or not passphrase
// set a temporary one
CAtlString strTmpKey = L"HostedNetwork12345";
hr = SetHostedNetworkKey(strTmpKey);
BAIL_ON_FAILURE(hr);
strKey = strTmpKey;
}
else
{
// got the key
strKey = strSecondaryKey;
}
error:
if (pucSecondaryKey != NULL)
{
WlanFreeMemory(pucSecondaryKey);
pucSecondaryKey = NULL;
}
return hr;
}
HRESULT
CWlanManager::StartHostedNetwork()
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
Lock();
if (!m_Initialized)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
// if (wlan_hosted_network_active == m_HostedNetworkState)
// {
// BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
// }
//
// Start hosted network
//
dwError = WlanHostedNetworkStartUsing(
m_WlanHandle,
NULL,
NULL
);
BAIL_ON_WIN32_ERROR(dwError, hr);
error:
Unlock();
return hr;
}
HRESULT
CWlanManager::StopHostedNetwork()
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
Lock();
if (!m_Initialized)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
if (m_HostedNetworkState != wlan_hosted_network_active)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
//
// Stop hosted network
//
dwError = WlanHostedNetworkStopUsing(
m_WlanHandle,
NULL,
NULL
);
BAIL_ON_WIN32_ERROR(dwError, hr);
error:
Unlock();
return hr;
}
HRESULT
CWlanManager::ForceStopHostedNetwork()
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
Lock();
if (!m_Initialized)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
if (m_HostedNetworkState != wlan_hosted_network_active)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
//
// Stop hosted network
//
dwError = WlanHostedNetworkForceStop(
m_WlanHandle,
NULL,
NULL
);
BAIL_ON_WIN32_ERROR(dwError, hr);
error:
Unlock();
return hr;
}
HRESULT
CWlanManager::GetStaionList(
CRefObjList<CWlanStation*>& StationList
)
{
HRESULT hr = S_OK;
Lock();
if (!m_Initialized)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
if (m_HostedNetworkState != wlan_hosted_network_active)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
//
// Remove old entries
//
StationList.RemoveAllEntries();
//
// Copy all the stations to the list
//
for (size_t i = 0; i < m_StationList.GetCount(); i++)
{
CWlanStation* pStation = m_StationList.GetAt(m_StationList.FindIndex(i));
_ASSERT(pStation != NULL);
pStation->AddRef();
StationList.AddTail(pStation);
}
error:
Unlock();
return hr;
}
HRESULT
CWlanManager::GetHostedNetworkInterfaceGuid(
GUID& InterfaceGuid
)
{
HRESULT hr = S_OK;
DWORD dwError = ERROR_SUCCESS;
PWLAN_HOSTED_NETWORK_STATUS pAPStatus = NULL; // hosted network status
//
// get the hosted network status
//
dwError = WlanHostedNetworkQueryStatus(
m_WlanHandle,
&pAPStatus,
NULL // reserved
);
BAIL_ON_WIN32_ERROR(dwError, hr);
InterfaceGuid = pAPStatus->IPDeviceID;
error:
if (pAPStatus != NULL)
{
WlanFreeMemory(pAPStatus);
pAPStatus = NULL;
}
return hr;
}
HRESULT
CWlanManager::AdviseHostedNetworkNotification(
CHostedNetworkNotificationSink * pSink
)
{
HRESULT hr = S_OK;
Lock();
if (NULL == pSink)
{
BAIL_ON_HRESULT_ERROR(hr, E_INVALIDARG);
}
if (!m_Initialized)
{
BAIL_ON_WIN32_ERROR(ERROR_INVALID_STATE, hr);
}
if (m_NotificationSink != NULL)
{
BAIL_ON_HRESULT_ERROR(hr, E_FAIL);
}
//
// Create callback complete event if needed
//
if (NULL == m_CallbackComplete)
{
m_CallbackComplete = CreateEvent(
NULL,
FALSE, // manual reset
TRUE, // initial state
NULL
);
if (NULL == m_CallbackComplete)
{
BAIL_ON_LAST_ERROR(hr);
}
}
//
// Set notification sink
//
m_NotificationSink = pSink;
//
// check if hosted network is supported
//
if (wlan_hosted_network_unavailable == m_HostedNetworkState)
{
pSink->OnHostedNetworkNotAvailable();
}
//
// Check if hosted network is enabled
//
if (wlan_hosted_network_active == m_HostedNetworkState)
{
for (unsigned int i = 0; i < m_StationList.GetCount(); i++)
{
CWlanStation * pStation = m_StationList.GetAt(m_StationList.FindIndex(i));
_ASSERT(pStation != NULL);
pSink->OnStationJoin(pStation);
}
}
error:
Unlock();
return hr;
}
HRESULT
CWlanManager::UnadviseHostedNetworkNotification()
{
HRESULT hr = S_OK;
HANDLE eCallbackComplete = NULL;
Lock();
if (m_NotificationSink != NULL)
{
m_NotificationSink = NULL;
_ASSERT(m_CallbackComplete != NULL);
eCallbackComplete = m_CallbackComplete;
}
else
{
hr = E_FAIL;
}
Unlock();
//
// Wait for callback to complete
//
if (eCallbackComplete != NULL)
{
WaitForSingleObject(eCallbackComplete, INFINITE);
}
return hr;
}
HRESULT
CWlanManager::IsHostedNetworkStarted(
bool & fStarted
)
{
HRESULT hr = S_OK;
Lock();
if (m_Initialized)
{
fStarted = (wlan_hosted_network_active == m_HostedNetworkState);
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
}
Unlock();
return hr;
}