808 lines
21 KiB
C
808 lines
21 KiB
C
/*
|
|
* 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;
|
|
}
|