Home | History | Annotate | Download | only in drivers
      1 /*
      2  * ndis_events - Receive NdisMIndicateStatus() events using WMI
      3  * Copyright (c) 2004-2006, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  */
     14 
     15 #define _WIN32_WINNT    0x0400
     16 
     17 #include "includes.h"
     18 
     19 #ifndef COBJMACROS
     20 #define COBJMACROS
     21 #endif /* COBJMACROS */
     22 #include <wbemidl.h>
     23 
     24 #include "common.h"
     25 
     26 
     27 static int wmi_refcnt = 0;
     28 static int wmi_first = 1;
     29 
     30 struct ndis_events_data {
     31 	IWbemObjectSink sink;
     32 	IWbemObjectSinkVtbl sink_vtbl;
     33 
     34 	IWbemServices *pSvc;
     35 	IWbemLocator *pLoc;
     36 
     37 	HANDLE read_pipe, write_pipe, event_avail;
     38 	UINT ref;
     39 	int terminating;
     40 	char *ifname; /* {GUID..} */
     41 	WCHAR *adapter_desc;
     42 };
     43 
     44 #define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
     45 #define BstrFree(x) if (x) SysFreeString(x)
     46 
     47 /* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
     48  * BSTRs */
     49 HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
     50 	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
     51 	long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
     52 {
     53 	BSTR bsQueryLanguage, bsQuery;
     54 	HRESULT hr;
     55 
     56 	bsQueryLanguage = BstrAlloc(strQueryLanguage);
     57 	bsQuery = BstrAlloc(strQuery);
     58 
     59 	hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
     60 				     pCtx, ppEnum);
     61 
     62 	BstrFree(bsQueryLanguage);
     63 	BstrFree(bsQuery);
     64 
     65 	return hr;
     66 }
     67 
     68 
     69 HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
     70 	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
     71 	long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
     72 {
     73 	BSTR bsQueryLanguage, bsQuery;
     74 	HRESULT hr;
     75 
     76 	bsQueryLanguage = BstrAlloc(strQueryLanguage);
     77 	bsQuery = BstrAlloc(strQuery);
     78 
     79 	hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
     80 						      bsQuery, lFlags, pCtx,
     81 						      pResponseHandler);
     82 
     83 	BstrFree(bsQueryLanguage);
     84 	BstrFree(bsQuery);
     85 
     86 	return hr;
     87 }
     88 
     89 
     90 HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
     91 	IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
     92 	LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
     93 	LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
     94 {
     95 	BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
     96 	HRESULT hr;
     97 
     98 	bsNetworkResource = BstrAlloc(strNetworkResource);
     99 	bsUser = BstrAlloc(strUser);
    100 	bsPassword = BstrAlloc(strPassword);
    101 	bsLocale = BstrAlloc(strLocale);
    102 	bsAuthority = BstrAlloc(strAuthority);
    103 
    104 	hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
    105 					bsPassword, bsLocale, lSecurityFlags,
    106 					bsAuthority, pCtx, ppNamespace);
    107 
    108 	BstrFree(bsNetworkResource);
    109 	BstrFree(bsUser);
    110 	BstrFree(bsPassword);
    111 	BstrFree(bsLocale);
    112 	BstrFree(bsAuthority);
    113 
    114 	return hr;
    115 }
    116 
    117 
    118 enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
    119 		   EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
    120 
    121 static int ndis_events_get_adapter(struct ndis_events_data *events,
    122 				   const char *ifname, const char *desc);
    123 
    124 
    125 static int ndis_events_constructor(struct ndis_events_data *events)
    126 {
    127 	events->ref = 1;
    128 
    129 	if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
    130 		wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
    131 			   (int) GetLastError());
    132 		return -1;
    133 	}
    134 	events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
    135 	if (events->event_avail == NULL) {
    136 		wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
    137 			   (int) GetLastError());
    138 		CloseHandle(events->read_pipe);
    139 		CloseHandle(events->write_pipe);
    140 		return -1;
    141 	}
    142 
    143 	return 0;
    144 }
    145 
    146 
    147 static void ndis_events_destructor(struct ndis_events_data *events)
    148 {
    149 	CloseHandle(events->read_pipe);
    150 	CloseHandle(events->write_pipe);
    151 	CloseHandle(events->event_avail);
    152 	IWbemServices_Release(events->pSvc);
    153 	IWbemLocator_Release(events->pLoc);
    154 	if (--wmi_refcnt == 0)
    155 		CoUninitialize();
    156 }
    157 
    158 
    159 static HRESULT STDMETHODCALLTYPE
    160 ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
    161 {
    162 	*obj = NULL;
    163 
    164 	if (IsEqualIID(riid, &IID_IUnknown) ||
    165 	    IsEqualIID(riid, &IID_IWbemObjectSink)) {
    166 		*obj = this;
    167 		IWbemObjectSink_AddRef(this);
    168 		return NOERROR;
    169 	}
    170 
    171 	return E_NOINTERFACE;
    172 }
    173 
    174 
    175 static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
    176 {
    177 	struct ndis_events_data *events = (struct ndis_events_data *) this;
    178 	return ++events->ref;
    179 }
    180 
    181 
    182 static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
    183 {
    184 	struct ndis_events_data *events = (struct ndis_events_data *) this;
    185 
    186 	if (--events->ref != 0)
    187 		return events->ref;
    188 
    189 	ndis_events_destructor(events);
    190 	wpa_printf(MSG_DEBUG, "ndis_events: terminated");
    191 	os_free(events->adapter_desc);
    192 	os_free(events->ifname);
    193 	os_free(events);
    194 	return 0;
    195 }
    196 
    197 
    198 static int ndis_events_send_event(struct ndis_events_data *events,
    199 				  enum event_types type,
    200 				  char *data, size_t data_len)
    201 {
    202 	char buf[512], *pos, *end;
    203 	int _type;
    204 	DWORD written;
    205 
    206 	end = buf + sizeof(buf);
    207 	_type = (int) type;
    208 	os_memcpy(buf, &_type, sizeof(_type));
    209 	pos = buf + sizeof(_type);
    210 
    211 	if (data) {
    212 		if (2 + data_len > (size_t) (end - pos)) {
    213 			wpa_printf(MSG_DEBUG, "Not enough room for send_event "
    214 				   "data (%d)", data_len);
    215 			return -1;
    216 		}
    217 		*pos++ = data_len >> 8;
    218 		*pos++ = data_len & 0xff;
    219 		os_memcpy(pos, data, data_len);
    220 		pos += data_len;
    221 	}
    222 
    223 	if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
    224 		SetEvent(events->event_avail);
    225 		return 0;
    226 	}
    227 	wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
    228 	return -1;
    229 }
    230 
    231 
    232 static void ndis_events_media_connect(struct ndis_events_data *events)
    233 {
    234 	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
    235 	ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
    236 }
    237 
    238 
    239 static void ndis_events_media_disconnect(struct ndis_events_data *events)
    240 {
    241 	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
    242 	ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
    243 }
    244 
    245 
    246 static void ndis_events_media_specific(struct ndis_events_data *events,
    247 				       IWbemClassObject *pObj)
    248 {
    249 	VARIANT vt;
    250 	HRESULT hr;
    251 	LONG lower, upper, k;
    252 	UCHAR ch;
    253 	char *data, *pos;
    254 	size_t data_len;
    255 
    256 	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
    257 
    258 	/* This is the StatusBuffer from NdisMIndicateStatus() call */
    259 	hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
    260 				  0, &vt, NULL, NULL);
    261 	if (FAILED(hr)) {
    262 		wpa_printf(MSG_DEBUG, "Could not get "
    263 			   "NdisStatusMediaSpecificIndication from "
    264 			   "the object?!");
    265 		return;
    266 	}
    267 
    268 	SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
    269 	SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
    270 	data_len = upper - lower + 1;
    271 	data = os_malloc(data_len);
    272 	if (data == NULL) {
    273 		wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
    274 			   "data");
    275 		VariantClear(&vt);
    276 		return;
    277 	}
    278 
    279 	pos = data;
    280 	for (k = lower; k <= upper; k++) {
    281 		SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
    282 		*pos++ = ch;
    283 	}
    284 	wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
    285 
    286 	VariantClear(&vt);
    287 
    288 	ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
    289 
    290 	os_free(data);
    291 }
    292 
    293 
    294 static void ndis_events_adapter_arrival(struct ndis_events_data *events)
    295 {
    296 	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
    297 	ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
    298 }
    299 
    300 
    301 static void ndis_events_adapter_removal(struct ndis_events_data *events)
    302 {
    303 	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
    304 	ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
    305 }
    306 
    307 
    308 static HRESULT STDMETHODCALLTYPE
    309 ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
    310 		     IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
    311 {
    312 	struct ndis_events_data *events = (struct ndis_events_data *) this;
    313 	long i;
    314 
    315 	if (events->terminating) {
    316 		wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
    317 			   "indication - terminating");
    318 		return WBEM_NO_ERROR;
    319 	}
    320 	/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
    321 	   lObjectCount); */
    322 
    323 	for (i = 0; i < lObjectCount; i++) {
    324 		IWbemClassObject *pObj = ppObjArray[i];
    325 		HRESULT hr;
    326 		VARIANT vtClass, vt;
    327 
    328 		hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
    329 					  NULL);
    330 		if (FAILED(hr)) {
    331 			wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
    332 				   "event.");
    333 			break;
    334 		}
    335 		/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
    336 
    337 		hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
    338 					  NULL);
    339 		if (FAILED(hr)) {
    340 			wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
    341 				   "from event.");
    342 			VariantClear(&vtClass);
    343 			break;
    344 		}
    345 
    346 		if (wcscmp(vtClass.bstrVal,
    347 			   L"MSNdis_NotifyAdapterArrival") == 0) {
    348 			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
    349 				   "update adapter description since it may "
    350 				   "have changed with new adapter instance");
    351 			ndis_events_get_adapter(events, events->ifname, NULL);
    352 		}
    353 
    354 		if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
    355 			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
    356 				   "indication for foreign adapter: "
    357 				   "InstanceName: '%S' __CLASS: '%S'",
    358 				   vt.bstrVal, vtClass.bstrVal);
    359 			VariantClear(&vtClass);
    360 			VariantClear(&vt);
    361 			continue;
    362 		}
    363 		VariantClear(&vt);
    364 
    365 		if (wcscmp(vtClass.bstrVal,
    366 			   L"MSNdis_StatusMediaSpecificIndication") == 0) {
    367 			ndis_events_media_specific(events, pObj);
    368 		} else if (wcscmp(vtClass.bstrVal,
    369 				  L"MSNdis_StatusMediaConnect") == 0) {
    370 			ndis_events_media_connect(events);
    371 		} else if (wcscmp(vtClass.bstrVal,
    372 				  L"MSNdis_StatusMediaDisconnect") == 0) {
    373 			ndis_events_media_disconnect(events);
    374 		} else if (wcscmp(vtClass.bstrVal,
    375 				  L"MSNdis_NotifyAdapterArrival") == 0) {
    376 			ndis_events_adapter_arrival(events);
    377 		} else if (wcscmp(vtClass.bstrVal,
    378 				  L"MSNdis_NotifyAdapterRemoval") == 0) {
    379 			ndis_events_adapter_removal(events);
    380 		} else {
    381 			wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
    382 				   "'%S'", vtClass.bstrVal);
    383 		}
    384 
    385 		VariantClear(&vtClass);
    386 	}
    387 
    388 	return WBEM_NO_ERROR;
    389 }
    390 
    391 
    392 static HRESULT STDMETHODCALLTYPE
    393 ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
    394 		       BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
    395 {
    396 	return WBEM_NO_ERROR;
    397 }
    398 
    399 
    400 static int notification_query(IWbemObjectSink *pDestSink,
    401 			      IWbemServices *pSvc, const char *class_name)
    402 {
    403 	HRESULT hr;
    404 	WCHAR query[256];
    405 
    406 	_snwprintf(query, 256,
    407 		  L"SELECT * FROM %S", class_name);
    408 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
    409 	hr = call_IWbemServices_ExecNotificationQueryAsync(
    410 		pSvc, L"WQL", query, 0, 0, pDestSink);
    411 	if (FAILED(hr)) {
    412 		wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
    413 			   "failed with hresult of 0x%x",
    414 			   class_name, (int) hr);
    415 		return -1;
    416 	}
    417 
    418 	return 0;
    419 }
    420 
    421 
    422 static int register_async_notification(IWbemObjectSink *pDestSink,
    423 				       IWbemServices *pSvc)
    424 {
    425 	int i;
    426 	const char *class_list[] = {
    427 		"MSNdis_StatusMediaConnect",
    428 		"MSNdis_StatusMediaDisconnect",
    429 		"MSNdis_StatusMediaSpecificIndication",
    430 		"MSNdis_NotifyAdapterArrival",
    431 		"MSNdis_NotifyAdapterRemoval",
    432 		NULL
    433 	};
    434 
    435 	for (i = 0; class_list[i]; i++) {
    436 		if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
    437 			return -1;
    438 	}
    439 
    440 	return 0;
    441 }
    442 
    443 
    444 void ndis_events_deinit(struct ndis_events_data *events)
    445 {
    446 	events->terminating = 1;
    447 	IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
    448 	IWbemObjectSink_Release(&events->sink);
    449 	/*
    450 	 * Rest of deinitialization is done in ndis_events_destructor() once
    451 	 * all reference count drops to zero.
    452 	 */
    453 }
    454 
    455 
    456 static int ndis_events_use_desc(struct ndis_events_data *events,
    457 				const char *desc)
    458 {
    459 	char *tmp, *pos;
    460 	size_t len;
    461 
    462 	if (desc == NULL) {
    463 		if (events->adapter_desc == NULL)
    464 			return -1;
    465 		/* Continue using old description */
    466 		return 0;
    467 	}
    468 
    469 	tmp = os_strdup(desc);
    470 	if (tmp == NULL)
    471 		return -1;
    472 
    473 	pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
    474 	if (pos)
    475 		*pos = '\0';
    476 
    477 	len = os_strlen(tmp);
    478 	events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
    479 	if (events->adapter_desc == NULL) {
    480 		os_free(tmp);
    481 		return -1;
    482 	}
    483 	_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
    484 	os_free(tmp);
    485 	return 0;
    486 }
    487 
    488 
    489 static int ndis_events_get_adapter(struct ndis_events_data *events,
    490 				   const char *ifname, const char *desc)
    491 {
    492 	HRESULT hr;
    493 	IWbemServices *pSvc;
    494 #define MAX_QUERY_LEN 256
    495 	WCHAR query[MAX_QUERY_LEN];
    496 	IEnumWbemClassObject *pEnumerator;
    497 	IWbemClassObject *pObj;
    498 	ULONG uReturned;
    499 	VARIANT vt;
    500 	int len, pos;
    501 
    502 	/*
    503 	 * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
    504 	 * to have better probability of matching with InstanceName from
    505 	 * MSNdis events. If this fails, use the provided description.
    506 	 */
    507 
    508 	os_free(events->adapter_desc);
    509 	events->adapter_desc = NULL;
    510 
    511 	hr = call_IWbemLocator_ConnectServer(
    512 		events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
    513 	if (FAILED(hr)) {
    514 		wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
    515 			   "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
    516 		return ndis_events_use_desc(events, desc);
    517 	}
    518 	wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
    519 
    520 	_snwprintf(query, MAX_QUERY_LEN,
    521 		  L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
    522 		  L"WHERE SettingID='%S'", ifname);
    523 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
    524 
    525 	hr = call_IWbemServices_ExecQuery(
    526 		pSvc, L"WQL", query,
    527 		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    528 		NULL, &pEnumerator);
    529 	if (!SUCCEEDED(hr)) {
    530 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
    531 			   "GUID from Win32_NetworkAdapterConfiguration: "
    532 			   "0x%x", (int) hr);
    533 		IWbemServices_Release(pSvc);
    534 		return ndis_events_use_desc(events, desc);
    535 	}
    536 
    537 	uReturned = 0;
    538 	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
    539 				       &pObj, &uReturned);
    540 	if (!SUCCEEDED(hr) || uReturned == 0) {
    541 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
    542 			   "GUID from Win32_NetworkAdapterConfiguration: "
    543 			   "0x%x", (int) hr);
    544 		IEnumWbemClassObject_Release(pEnumerator);
    545 		IWbemServices_Release(pSvc);
    546 		return ndis_events_use_desc(events, desc);
    547 	}
    548 	IEnumWbemClassObject_Release(pEnumerator);
    549 
    550 	VariantInit(&vt);
    551 	hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
    552 	if (!SUCCEEDED(hr)) {
    553 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
    554 			   "Win32_NetworkAdapterConfiguration: 0x%x",
    555 			   (int) hr);
    556 		IWbemServices_Release(pSvc);
    557 		return ndis_events_use_desc(events, desc);
    558 	}
    559 
    560 	_snwprintf(query, MAX_QUERY_LEN,
    561 		  L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
    562 		  L"Index=%d",
    563 		  vt.uintVal);
    564 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
    565 	VariantClear(&vt);
    566 	IWbemClassObject_Release(pObj);
    567 
    568 	hr = call_IWbemServices_ExecQuery(
    569 		pSvc, L"WQL", query,
    570 		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    571 		NULL, &pEnumerator);
    572 	if (!SUCCEEDED(hr)) {
    573 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
    574 			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
    575 		IWbemServices_Release(pSvc);
    576 		return ndis_events_use_desc(events, desc);
    577 	}
    578 
    579 	uReturned = 0;
    580 	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
    581 				       &pObj, &uReturned);
    582 	if (!SUCCEEDED(hr) || uReturned == 0) {
    583 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
    584 			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
    585 		IEnumWbemClassObject_Release(pEnumerator);
    586 		IWbemServices_Release(pSvc);
    587 		return ndis_events_use_desc(events, desc);
    588 	}
    589 	IEnumWbemClassObject_Release(pEnumerator);
    590 
    591 	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
    592 	if (!SUCCEEDED(hr)) {
    593 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
    594 			   "Win32_NetworkAdapter: 0x%x", (int) hr);
    595 		IWbemClassObject_Release(pObj);
    596 		IWbemServices_Release(pSvc);
    597 		return ndis_events_use_desc(events, desc);
    598 	}
    599 
    600 	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
    601 		   vt.bstrVal);
    602 	events->adapter_desc = _wcsdup(vt.bstrVal);
    603 	VariantClear(&vt);
    604 
    605 	/*
    606 	 * Try to get even better candidate for matching with InstanceName
    607 	 * from Win32_PnPEntity. This is needed at least for some USB cards
    608 	 * that can change the InstanceName whenever being unplugged and
    609 	 * plugged again.
    610 	 */
    611 
    612 	hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
    613 	if (!SUCCEEDED(hr)) {
    614 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
    615 			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
    616 		IWbemClassObject_Release(pObj);
    617 		IWbemServices_Release(pSvc);
    618 		if (events->adapter_desc == NULL)
    619 			return ndis_events_use_desc(events, desc);
    620 		return 0; /* use Win32_NetworkAdapter::Name */
    621 	}
    622 
    623 	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
    624 		   "'%S'", vt.bstrVal);
    625 
    626 	len = _snwprintf(query, MAX_QUERY_LEN,
    627 			L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
    628 	if (len < 0 || len >= MAX_QUERY_LEN - 1) {
    629 		VariantClear(&vt);
    630 		IWbemClassObject_Release(pObj);
    631 		IWbemServices_Release(pSvc);
    632 		if (events->adapter_desc == NULL)
    633 			return ndis_events_use_desc(events, desc);
    634 		return 0; /* use Win32_NetworkAdapter::Name */
    635 	}
    636 
    637 	/* Escape \ as \\ */
    638 	for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
    639 		if (vt.bstrVal[pos] == '\\') {
    640 			if (len >= MAX_QUERY_LEN - 3)
    641 				break;
    642 			query[len++] = '\\';
    643 		}
    644 		query[len++] = vt.bstrVal[pos];
    645 	}
    646 	query[len++] = L'\'';
    647 	query[len] = L'\0';
    648 	VariantClear(&vt);
    649 	IWbemClassObject_Release(pObj);
    650 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
    651 
    652 	hr = call_IWbemServices_ExecQuery(
    653 		pSvc, L"WQL", query,
    654 		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
    655 		NULL, &pEnumerator);
    656 	if (!SUCCEEDED(hr)) {
    657 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
    658 			   "Name from Win32_PnPEntity: 0x%x", (int) hr);
    659 		IWbemServices_Release(pSvc);
    660 		if (events->adapter_desc == NULL)
    661 			return ndis_events_use_desc(events, desc);
    662 		return 0; /* use Win32_NetworkAdapter::Name */
    663 	}
    664 
    665 	uReturned = 0;
    666 	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
    667 				       &pObj, &uReturned);
    668 	if (!SUCCEEDED(hr) || uReturned == 0) {
    669 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
    670 			   "from Win32_PnPEntity: 0x%x", (int) hr);
    671 		IEnumWbemClassObject_Release(pEnumerator);
    672 		IWbemServices_Release(pSvc);
    673 		if (events->adapter_desc == NULL)
    674 			return ndis_events_use_desc(events, desc);
    675 		return 0; /* use Win32_NetworkAdapter::Name */
    676 	}
    677 	IEnumWbemClassObject_Release(pEnumerator);
    678 
    679 	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
    680 	if (!SUCCEEDED(hr)) {
    681 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
    682 			   "Win32_PnPEntity: 0x%x", (int) hr);
    683 		IWbemClassObject_Release(pObj);
    684 		IWbemServices_Release(pSvc);
    685 		if (events->adapter_desc == NULL)
    686 			return ndis_events_use_desc(events, desc);
    687 		return 0; /* use Win32_NetworkAdapter::Name */
    688 	}
    689 
    690 	wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
    691 		   vt.bstrVal);
    692 	os_free(events->adapter_desc);
    693 	events->adapter_desc = _wcsdup(vt.bstrVal);
    694 	VariantClear(&vt);
    695 
    696 	IWbemClassObject_Release(pObj);
    697 
    698 	IWbemServices_Release(pSvc);
    699 
    700 	if (events->adapter_desc == NULL)
    701 		return ndis_events_use_desc(events, desc);
    702 
    703 	return 0;
    704 }
    705 
    706 
    707 struct ndis_events_data *
    708 ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
    709 		 const char *ifname, const char *desc)
    710 {
    711 	HRESULT hr;
    712 	IWbemObjectSink *pSink;
    713 	struct ndis_events_data *events;
    714 
    715 	events = os_zalloc(sizeof(*events));
    716 	if (events == NULL) {
    717 		wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
    718 		return NULL;
    719 	}
    720 	events->ifname = os_strdup(ifname);
    721 	if (events->ifname == NULL) {
    722 		os_free(events);
    723 		return NULL;
    724 	}
    725 
    726 	if (wmi_refcnt++ == 0) {
    727 		hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    728 		if (FAILED(hr)) {
    729 			wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
    730 				   "returned 0x%x", (int) hr);
    731 			os_free(events);
    732 			return NULL;
    733 		}
    734 	}
    735 
    736 	if (wmi_first) {
    737 		/* CoInitializeSecurity() must be called once and only once
    738 		 * per process, so let's use wmi_first flag to protect against
    739 		 * multiple calls. */
    740 		wmi_first = 0;
    741 
    742 		hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
    743 					  RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
    744 					  RPC_C_IMP_LEVEL_IMPERSONATE,
    745 					  NULL, EOAC_SECURE_REFS, NULL);
    746 		if (FAILED(hr)) {
    747 			wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
    748 				   "- returned 0x%x", (int) hr);
    749 			os_free(events);
    750 			return NULL;
    751 		}
    752 	}
    753 
    754 	hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
    755 			      &IID_IWbemLocator,
    756 			      (LPVOID *) (void *) &events->pLoc);
    757 	if (FAILED(hr)) {
    758 		wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
    759 			   "0x%x", (int) hr);
    760 		CoUninitialize();
    761 		os_free(events);
    762 		return NULL;
    763 	}
    764 
    765 	if (ndis_events_get_adapter(events, ifname, desc) < 0) {
    766 		CoUninitialize();
    767 		os_free(events);
    768 		return NULL;
    769 	}
    770 	wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
    771 		   events->adapter_desc);
    772 
    773 	hr = call_IWbemLocator_ConnectServer(
    774 		events->pLoc, L"ROOT\\WMI", NULL, NULL,
    775 		0, 0, 0, 0, &events->pSvc);
    776 	if (FAILED(hr)) {
    777 		wpa_printf(MSG_ERROR, "Could not connect to server - error "
    778 			   "0x%x", (int) hr);
    779 		CoUninitialize();
    780 		os_free(events->adapter_desc);
    781 		os_free(events);
    782 		return NULL;
    783 	}
    784 	wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
    785 
    786 	ndis_events_constructor(events);
    787 	pSink = &events->sink;
    788 	pSink->lpVtbl = &events->sink_vtbl;
    789 	events->sink_vtbl.QueryInterface = ndis_events_query_interface;
    790 	events->sink_vtbl.AddRef = ndis_events_add_ref;
    791 	events->sink_vtbl.Release = ndis_events_release;
    792 	events->sink_vtbl.Indicate = ndis_events_indicate;
    793 	events->sink_vtbl.SetStatus = ndis_events_set_status;
    794 
    795 	if (register_async_notification(pSink, events->pSvc) < 0) {
    796 		wpa_printf(MSG_DEBUG, "Failed to register async "
    797 			   "notifications");
    798 		ndis_events_destructor(events);
    799 		os_free(events->adapter_desc);
    800 		os_free(events);
    801 		return NULL;
    802 	}
    803 
    804 	*read_pipe = events->read_pipe;
    805 	*event_avail = events->event_avail;
    806 
    807 	return events;
    808 }
    809