blob: f6eaa7c9f7ee2734f351d53436b9d5970147e933 [file] [log] [blame]
/*
* ndis_events - Receive NdisMIndicateStatus() events using WMI
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#define _WIN32_WINNT 0x0400
#include "includes.h"
#ifndef COBJMACROS
#define COBJMACROS
#endif /* COBJMACROS */
#include <wbemidl.h>
#include "common.h"
static int wmi_refcnt = 0;
static int wmi_first = 1;
struct ndis_events_data {
IWbemObjectSink sink;
IWbemObjectSinkVtbl sink_vtbl;
IWbemServices *pSvc;
IWbemLocator *pLoc;
HANDLE read_pipe, write_pipe, event_avail;
UINT ref;
int terminating;
char *ifname; /* {GUID..} */
WCHAR *adapter_desc;
};
#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
#define BstrFree(x) if (x) SysFreeString(x)
/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
* BSTRs */
HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
{
BSTR bsQueryLanguage, bsQuery;
HRESULT hr;
bsQueryLanguage = BstrAlloc(strQueryLanguage);
bsQuery = BstrAlloc(strQuery);
hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
pCtx, ppEnum);
BstrFree(bsQueryLanguage);
BstrFree(bsQuery);
return hr;
}
HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
BSTR bsQueryLanguage, bsQuery;
HRESULT hr;
bsQueryLanguage = BstrAlloc(strQueryLanguage);
bsQuery = BstrAlloc(strQuery);
hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
bsQuery, lFlags, pCtx,
pResponseHandler);
BstrFree(bsQueryLanguage);
BstrFree(bsQuery);
return hr;
}
HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
{
BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
HRESULT hr;
bsNetworkResource = BstrAlloc(strNetworkResource);
bsUser = BstrAlloc(strUser);
bsPassword = BstrAlloc(strPassword);
bsLocale = BstrAlloc(strLocale);
bsAuthority = BstrAlloc(strAuthority);
hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
bsPassword, bsLocale, lSecurityFlags,
bsAuthority, pCtx, ppNamespace);
BstrFree(bsNetworkResource);
BstrFree(bsUser);
BstrFree(bsPassword);
BstrFree(bsLocale);
BstrFree(bsAuthority);
return hr;
}
enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
static int ndis_events_get_adapter(struct ndis_events_data *events,
const char *ifname, const char *desc);
static int ndis_events_constructor(struct ndis_events_data *events)
{
events->ref = 1;
if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
(int) GetLastError());
return -1;
}
events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
if (events->event_avail == NULL) {
wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
(int) GetLastError());
CloseHandle(events->read_pipe);
CloseHandle(events->write_pipe);
return -1;
}
return 0;
}
static void ndis_events_destructor(struct ndis_events_data *events)
{
CloseHandle(events->read_pipe);
CloseHandle(events->write_pipe);
CloseHandle(events->event_avail);
IWbemServices_Release(events->pSvc);
IWbemLocator_Release(events->pLoc);
if (--wmi_refcnt == 0)
CoUninitialize();
}
static HRESULT STDMETHODCALLTYPE
ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
{
*obj = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IWbemObjectSink)) {
*obj = this;
IWbemObjectSink_AddRef(this);
return NOERROR;
}
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
{
struct ndis_events_data *events = (struct ndis_events_data *) this;
return ++events->ref;
}
static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
{
struct ndis_events_data *events = (struct ndis_events_data *) this;
if (--events->ref != 0)
return events->ref;
ndis_events_destructor(events);
wpa_printf(MSG_DEBUG, "ndis_events: terminated");
os_free(events->adapter_desc);
os_free(events->ifname);
os_free(events);
return 0;
}
static int ndis_events_send_event(struct ndis_events_data *events,
enum event_types type,
char *data, size_t data_len)
{
char buf[512], *pos, *end;
int _type;
DWORD written;
end = buf + sizeof(buf);
_type = (int) type;
os_memcpy(buf, &_type, sizeof(_type));
pos = buf + sizeof(_type);
if (data) {
if (2 + data_len > (size_t) (end - pos)) {
wpa_printf(MSG_DEBUG, "Not enough room for send_event "
"data (%d)", data_len);
return -1;
}
*pos++ = data_len >> 8;
*pos++ = data_len & 0xff;
os_memcpy(pos, data, data_len);
pos += data_len;
}
if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
SetEvent(events->event_avail);
return 0;
}
wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
return -1;
}
static void ndis_events_media_connect(struct ndis_events_data *events)
{
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
}
static void ndis_events_media_disconnect(struct ndis_events_data *events)
{
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
}
static void ndis_events_media_specific(struct ndis_events_data *events,
IWbemClassObject *pObj)
{
VARIANT vt;
HRESULT hr;
LONG lower, upper, k;
UCHAR ch;
char *data, *pos;
size_t data_len;
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
/* This is the StatusBuffer from NdisMIndicateStatus() call */
hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
0, &vt, NULL, NULL);
if (FAILED(hr)) {
wpa_printf(MSG_DEBUG, "Could not get "
"NdisStatusMediaSpecificIndication from "
"the object?!");
return;
}
SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
data_len = upper - lower + 1;
data = os_malloc(data_len);
if (data == NULL) {
wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
"data");
VariantClear(&vt);
return;
}
pos = data;
for (k = lower; k <= upper; k++) {
SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
*pos++ = ch;
}
wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
VariantClear(&vt);
ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
os_free(data);
}
static void ndis_events_adapter_arrival(struct ndis_events_data *events)
{
wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
}
static void ndis_events_adapter_removal(struct ndis_events_data *events)
{
wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
}
static HRESULT STDMETHODCALLTYPE
ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
{
struct ndis_events_data *events = (struct ndis_events_data *) this;
long i;
if (events->terminating) {
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
"indication - terminating");
return WBEM_NO_ERROR;
}
/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
lObjectCount); */
for (i = 0; i < lObjectCount; i++) {
IWbemClassObject *pObj = ppObjArray[i];
HRESULT hr;
VARIANT vtClass, vt;
hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
NULL);
if (FAILED(hr)) {
wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
"event.");
break;
}
/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
NULL);
if (FAILED(hr)) {
wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
"from event.");
VariantClear(&vtClass);
break;
}
if (wcscmp(vtClass.bstrVal,
L"MSNdis_NotifyAdapterArrival") == 0) {
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
"update adapter description since it may "
"have changed with new adapter instance");
ndis_events_get_adapter(events, events->ifname, NULL);
}
if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
"indication for foreign adapter: "
"InstanceName: '%S' __CLASS: '%S'",
vt.bstrVal, vtClass.bstrVal);
VariantClear(&vtClass);
VariantClear(&vt);
continue;
}
VariantClear(&vt);
if (wcscmp(vtClass.bstrVal,
L"MSNdis_StatusMediaSpecificIndication") == 0) {
ndis_events_media_specific(events, pObj);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_StatusMediaConnect") == 0) {
ndis_events_media_connect(events);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_StatusMediaDisconnect") == 0) {
ndis_events_media_disconnect(events);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_NotifyAdapterArrival") == 0) {
ndis_events_adapter_arrival(events);
} else if (wcscmp(vtClass.bstrVal,
L"MSNdis_NotifyAdapterRemoval") == 0) {
ndis_events_adapter_removal(events);
} else {
wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
"'%S'", vtClass.bstrVal);
}
VariantClear(&vtClass);
}
return WBEM_NO_ERROR;
}
static HRESULT STDMETHODCALLTYPE
ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
{
return WBEM_NO_ERROR;
}
static int notification_query(IWbemObjectSink *pDestSink,
IWbemServices *pSvc, const char *class_name)
{
HRESULT hr;
WCHAR query[256];
_snwprintf(query, 256,
L"SELECT * FROM %S", class_name);
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
hr = call_IWbemServices_ExecNotificationQueryAsync(
pSvc, L"WQL", query, 0, 0, pDestSink);
if (FAILED(hr)) {
wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
"failed with hresult of 0x%x",
class_name, (int) hr);
return -1;
}
return 0;
}
static int register_async_notification(IWbemObjectSink *pDestSink,
IWbemServices *pSvc)
{
int i;
const char *class_list[] = {
"MSNdis_StatusMediaConnect",
"MSNdis_StatusMediaDisconnect",
"MSNdis_StatusMediaSpecificIndication",
"MSNdis_NotifyAdapterArrival",
"MSNdis_NotifyAdapterRemoval",
NULL
};
for (i = 0; class_list[i]; i++) {
if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
return -1;
}
return 0;
}
void ndis_events_deinit(struct ndis_events_data *events)
{
events->terminating = 1;
IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
IWbemObjectSink_Release(&events->sink);
/*
* Rest of deinitialization is done in ndis_events_destructor() once
* all reference count drops to zero.
*/
}
static int ndis_events_use_desc(struct ndis_events_data *events,
const char *desc)
{
char *tmp, *pos;
size_t len;
if (desc == NULL) {
if (events->adapter_desc == NULL)
return -1;
/* Continue using old description */
return 0;
}
tmp = os_strdup(desc);
if (tmp == NULL)
return -1;
pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
if (pos)
*pos = '\0';
len = os_strlen(tmp);
events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
if (events->adapter_desc == NULL) {
os_free(tmp);
return -1;
}
_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
os_free(tmp);
return 0;
}
static int ndis_events_get_adapter(struct ndis_events_data *events,
const char *ifname, const char *desc)
{
HRESULT hr;
IWbemServices *pSvc;
#define MAX_QUERY_LEN 256
WCHAR query[MAX_QUERY_LEN];
IEnumWbemClassObject *pEnumerator;
IWbemClassObject *pObj;
ULONG uReturned;
VARIANT vt;
int len, pos;
/*
* Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
* to have better probability of matching with InstanceName from
* MSNdis events. If this fails, use the provided description.
*/
os_free(events->adapter_desc);
events->adapter_desc = NULL;
hr = call_IWbemLocator_ConnectServer(
events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
"server (ROOT\\CIMV2) - error 0x%x", (int) hr);
return ndis_events_use_desc(events, desc);
}
wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
_snwprintf(query, MAX_QUERY_LEN,
L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
L"WHERE SettingID='%S'", ifname);
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
hr = call_IWbemServices_ExecQuery(
pSvc, L"WQL", query,
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL, &pEnumerator);
if (!SUCCEEDED(hr)) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
"GUID from Win32_NetworkAdapterConfiguration: "
"0x%x", (int) hr);
IWbemServices_Release(pSvc);
return ndis_events_use_desc(events, desc);
}
uReturned = 0;
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
&pObj, &uReturned);
if (!SUCCEEDED(hr) || uReturned == 0) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
"GUID from Win32_NetworkAdapterConfiguration: "
"0x%x", (int) hr);
IEnumWbemClassObject_Release(pEnumerator);
IWbemServices_Release(pSvc);
return ndis_events_use_desc(events, desc);
}
IEnumWbemClassObject_Release(pEnumerator);
VariantInit(&vt);
hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
if (!SUCCEEDED(hr)) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
"Win32_NetworkAdapterConfiguration: 0x%x",
(int) hr);
IWbemServices_Release(pSvc);
return ndis_events_use_desc(events, desc);
}
_snwprintf(query, MAX_QUERY_LEN,
L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
L"Index=%d",
vt.uintVal);
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
VariantClear(&vt);
IWbemClassObject_Release(pObj);
hr = call_IWbemServices_ExecQuery(
pSvc, L"WQL", query,
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL, &pEnumerator);
if (!SUCCEEDED(hr)) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
"from Win32_NetworkAdapter: 0x%x", (int) hr);
IWbemServices_Release(pSvc);
return ndis_events_use_desc(events, desc);
}
uReturned = 0;
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
&pObj, &uReturned);
if (!SUCCEEDED(hr) || uReturned == 0) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
"from Win32_NetworkAdapter: 0x%x", (int) hr);
IEnumWbemClassObject_Release(pEnumerator);
IWbemServices_Release(pSvc);
return ndis_events_use_desc(events, desc);
}
IEnumWbemClassObject_Release(pEnumerator);
hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
if (!SUCCEEDED(hr)) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
"Win32_NetworkAdapter: 0x%x", (int) hr);
IWbemClassObject_Release(pObj);
IWbemServices_Release(pSvc);
return ndis_events_use_desc(events, desc);
}
wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
vt.bstrVal);
events->adapter_desc = _wcsdup(vt.bstrVal);
VariantClear(&vt);
/*
* Try to get even better candidate for matching with InstanceName
* from Win32_PnPEntity. This is needed at least for some USB cards
* that can change the InstanceName whenever being unplugged and
* plugged again.
*/
hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
if (!SUCCEEDED(hr)) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
"from Win32_NetworkAdapter: 0x%x", (int) hr);
IWbemClassObject_Release(pObj);
IWbemServices_Release(pSvc);
if (events->adapter_desc == NULL)
return ndis_events_use_desc(events, desc);
return 0; /* use Win32_NetworkAdapter::Name */
}
wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
"'%S'", vt.bstrVal);
len = _snwprintf(query, MAX_QUERY_LEN,
L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
if (len < 0 || len >= MAX_QUERY_LEN - 1) {
VariantClear(&vt);
IWbemClassObject_Release(pObj);
IWbemServices_Release(pSvc);
if (events->adapter_desc == NULL)
return ndis_events_use_desc(events, desc);
return 0; /* use Win32_NetworkAdapter::Name */
}
/* Escape \ as \\ */
for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
if (vt.bstrVal[pos] == '\\') {
if (len >= MAX_QUERY_LEN - 3)
break;
query[len++] = '\\';
}
query[len++] = vt.bstrVal[pos];
}
query[len++] = L'\'';
query[len] = L'\0';
VariantClear(&vt);
IWbemClassObject_Release(pObj);
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
hr = call_IWbemServices_ExecQuery(
pSvc, L"WQL", query,
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL, &pEnumerator);
if (!SUCCEEDED(hr)) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
"Name from Win32_PnPEntity: 0x%x", (int) hr);
IWbemServices_Release(pSvc);
if (events->adapter_desc == NULL)
return ndis_events_use_desc(events, desc);
return 0; /* use Win32_NetworkAdapter::Name */
}
uReturned = 0;
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
&pObj, &uReturned);
if (!SUCCEEDED(hr) || uReturned == 0) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
"from Win32_PnPEntity: 0x%x", (int) hr);
IEnumWbemClassObject_Release(pEnumerator);
IWbemServices_Release(pSvc);
if (events->adapter_desc == NULL)
return ndis_events_use_desc(events, desc);
return 0; /* use Win32_NetworkAdapter::Name */
}
IEnumWbemClassObject_Release(pEnumerator);
hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
if (!SUCCEEDED(hr)) {
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
"Win32_PnPEntity: 0x%x", (int) hr);
IWbemClassObject_Release(pObj);
IWbemServices_Release(pSvc);
if (events->adapter_desc == NULL)
return ndis_events_use_desc(events, desc);
return 0; /* use Win32_NetworkAdapter::Name */
}
wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
vt.bstrVal);
os_free(events->adapter_desc);
events->adapter_desc = _wcsdup(vt.bstrVal);
VariantClear(&vt);
IWbemClassObject_Release(pObj);
IWbemServices_Release(pSvc);
if (events->adapter_desc == NULL)
return ndis_events_use_desc(events, desc);
return 0;
}
struct ndis_events_data *
ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
const char *ifname, const char *desc)
{
HRESULT hr;
IWbemObjectSink *pSink;
struct ndis_events_data *events;
events = os_zalloc(sizeof(*events));
if (events == NULL) {
wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
return NULL;
}
events->ifname = os_strdup(ifname);
if (events->ifname == NULL) {
os_free(events);
return NULL;
}
if (wmi_refcnt++ == 0) {
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
"returned 0x%x", (int) hr);
os_free(events);
return NULL;
}
}
if (wmi_first) {
/* CoInitializeSecurity() must be called once and only once
* per process, so let's use wmi_first flag to protect against
* multiple calls. */
wmi_first = 0;
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_SECURE_REFS, NULL);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
"- returned 0x%x", (int) hr);
os_free(events);
return NULL;
}
}
hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
&IID_IWbemLocator,
(LPVOID *) (void *) &events->pLoc);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
"0x%x", (int) hr);
CoUninitialize();
os_free(events);
return NULL;
}
if (ndis_events_get_adapter(events, ifname, desc) < 0) {
CoUninitialize();
os_free(events);
return NULL;
}
wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
events->adapter_desc);
hr = call_IWbemLocator_ConnectServer(
events->pLoc, L"ROOT\\WMI", NULL, NULL,
0, 0, 0, 0, &events->pSvc);
if (FAILED(hr)) {
wpa_printf(MSG_ERROR, "Could not connect to server - error "
"0x%x", (int) hr);
CoUninitialize();
os_free(events->adapter_desc);
os_free(events);
return NULL;
}
wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
ndis_events_constructor(events);
pSink = &events->sink;
pSink->lpVtbl = &events->sink_vtbl;
events->sink_vtbl.QueryInterface = ndis_events_query_interface;
events->sink_vtbl.AddRef = ndis_events_add_ref;
events->sink_vtbl.Release = ndis_events_release;
events->sink_vtbl.Indicate = ndis_events_indicate;
events->sink_vtbl.SetStatus = ndis_events_set_status;
if (register_async_notification(pSink, events->pSvc) < 0) {
wpa_printf(MSG_DEBUG, "Failed to register async "
"notifications");
ndis_events_destructor(events);
os_free(events->adapter_desc);
os_free(events);
return NULL;
}
*read_pipe = events->read_pipe;
*event_avail = events->event_avail;
return events;
}