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