wpa_supplicant: Initial Revision 0.8.X

Based on:
commit 0725cc7b7efc434910e89865c42eda7ce61bbf08
Author: Jouni Malinen <j@w1.fi>
Date:   Thu Apr 21 20:41:01 2011 +0300

    Enable CONFIG_DRIVER_NL80211=y in the default configuration

    nl80211 should be preferred over WEXT with any recent Linux
    kernel version.

Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/drivers/ndis_events.c b/src/drivers/ndis_events.c
new file mode 100644
index 0000000..f6eaa7c
--- /dev/null
+++ b/src/drivers/ndis_events.c
@@ -0,0 +1,808 @@
+/*
+ * 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;
+}