xref: /freebsd/contrib/wpa/src/drivers/ndis_events.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*
2  * ndis_events - Receive NdisMIndicateStatus() events using WMI
3  * Copyright (c) 2004-2006, Jouni Malinen <j@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,
376 				   "Unexpected event - __CLASS: '%S'",
377 				   vtClass.bstrVal);
378 		}
379 
380 		VariantClear(&vtClass);
381 	}
382 
383 	return WBEM_NO_ERROR;
384 }
385 
386 
387 static HRESULT STDMETHODCALLTYPE
388 ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
389 		       BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
390 {
391 	return WBEM_NO_ERROR;
392 }
393 
394 
395 static int notification_query(IWbemObjectSink *pDestSink,
396 			      IWbemServices *pSvc, const char *class_name)
397 {
398 	HRESULT hr;
399 	WCHAR query[256];
400 
401 	_snwprintf(query, 256,
402 		  L"SELECT * FROM %S", class_name);
403 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
404 	hr = call_IWbemServices_ExecNotificationQueryAsync(
405 		pSvc, L"WQL", query, 0, 0, pDestSink);
406 	if (FAILED(hr)) {
407 		wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
408 			   "failed with hresult of 0x%x",
409 			   class_name, (int) hr);
410 		return -1;
411 	}
412 
413 	return 0;
414 }
415 
416 
417 static int register_async_notification(IWbemObjectSink *pDestSink,
418 				       IWbemServices *pSvc)
419 {
420 	int i;
421 	const char *class_list[] = {
422 		"MSNdis_StatusMediaConnect",
423 		"MSNdis_StatusMediaDisconnect",
424 		"MSNdis_StatusMediaSpecificIndication",
425 		"MSNdis_NotifyAdapterArrival",
426 		"MSNdis_NotifyAdapterRemoval",
427 		NULL
428 	};
429 
430 	for (i = 0; class_list[i]; i++) {
431 		if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
432 			return -1;
433 	}
434 
435 	return 0;
436 }
437 
438 
439 void ndis_events_deinit(struct ndis_events_data *events)
440 {
441 	events->terminating = 1;
442 	IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
443 	IWbemObjectSink_Release(&events->sink);
444 	/*
445 	 * Rest of deinitialization is done in ndis_events_destructor() once
446 	 * all reference count drops to zero.
447 	 */
448 }
449 
450 
451 static int ndis_events_use_desc(struct ndis_events_data *events,
452 				const char *desc)
453 {
454 	char *tmp, *pos;
455 	size_t len;
456 
457 	if (desc == NULL) {
458 		if (events->adapter_desc == NULL)
459 			return -1;
460 		/* Continue using old description */
461 		return 0;
462 	}
463 
464 	tmp = os_strdup(desc);
465 	if (tmp == NULL)
466 		return -1;
467 
468 	pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
469 	if (pos)
470 		*pos = '\0';
471 
472 	len = os_strlen(tmp);
473 	events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
474 	if (events->adapter_desc == NULL) {
475 		os_free(tmp);
476 		return -1;
477 	}
478 	_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
479 	os_free(tmp);
480 	return 0;
481 }
482 
483 
484 static int ndis_events_get_adapter(struct ndis_events_data *events,
485 				   const char *ifname, const char *desc)
486 {
487 	HRESULT hr;
488 	IWbemServices *pSvc;
489 #define MAX_QUERY_LEN 256
490 	WCHAR query[MAX_QUERY_LEN];
491 	IEnumWbemClassObject *pEnumerator;
492 	IWbemClassObject *pObj;
493 	ULONG uReturned;
494 	VARIANT vt;
495 	int len, pos;
496 
497 	/*
498 	 * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
499 	 * to have better probability of matching with InstanceName from
500 	 * MSNdis events. If this fails, use the provided description.
501 	 */
502 
503 	os_free(events->adapter_desc);
504 	events->adapter_desc = NULL;
505 
506 	hr = call_IWbemLocator_ConnectServer(
507 		events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
508 	if (FAILED(hr)) {
509 		wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
510 			   "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
511 		return ndis_events_use_desc(events, desc);
512 	}
513 	wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
514 
515 	_snwprintf(query, MAX_QUERY_LEN,
516 		  L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
517 		  L"WHERE SettingID='%S'", ifname);
518 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
519 
520 	hr = call_IWbemServices_ExecQuery(
521 		pSvc, L"WQL", query,
522 		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
523 		NULL, &pEnumerator);
524 	if (!SUCCEEDED(hr)) {
525 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
526 			   "GUID from Win32_NetworkAdapterConfiguration: "
527 			   "0x%x", (int) hr);
528 		IWbemServices_Release(pSvc);
529 		return ndis_events_use_desc(events, desc);
530 	}
531 
532 	uReturned = 0;
533 	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
534 				       &pObj, &uReturned);
535 	if (!SUCCEEDED(hr) || uReturned == 0) {
536 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
537 			   "GUID from Win32_NetworkAdapterConfiguration: "
538 			   "0x%x", (int) hr);
539 		IEnumWbemClassObject_Release(pEnumerator);
540 		IWbemServices_Release(pSvc);
541 		return ndis_events_use_desc(events, desc);
542 	}
543 	IEnumWbemClassObject_Release(pEnumerator);
544 
545 	VariantInit(&vt);
546 	hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
547 	if (!SUCCEEDED(hr)) {
548 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
549 			   "Win32_NetworkAdapterConfiguration: 0x%x",
550 			   (int) hr);
551 		IWbemServices_Release(pSvc);
552 		return ndis_events_use_desc(events, desc);
553 	}
554 
555 	_snwprintf(query, MAX_QUERY_LEN,
556 		  L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
557 		  L"Index=%d",
558 		  vt.uintVal);
559 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
560 	VariantClear(&vt);
561 	IWbemClassObject_Release(pObj);
562 
563 	hr = call_IWbemServices_ExecQuery(
564 		pSvc, L"WQL", query,
565 		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
566 		NULL, &pEnumerator);
567 	if (!SUCCEEDED(hr)) {
568 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
569 			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
570 		IWbemServices_Release(pSvc);
571 		return ndis_events_use_desc(events, desc);
572 	}
573 
574 	uReturned = 0;
575 	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
576 				       &pObj, &uReturned);
577 	if (!SUCCEEDED(hr) || uReturned == 0) {
578 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
579 			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
580 		IEnumWbemClassObject_Release(pEnumerator);
581 		IWbemServices_Release(pSvc);
582 		return ndis_events_use_desc(events, desc);
583 	}
584 	IEnumWbemClassObject_Release(pEnumerator);
585 
586 	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
587 	if (!SUCCEEDED(hr)) {
588 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
589 			   "Win32_NetworkAdapter: 0x%x", (int) hr);
590 		IWbemClassObject_Release(pObj);
591 		IWbemServices_Release(pSvc);
592 		return ndis_events_use_desc(events, desc);
593 	}
594 
595 	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
596 		   vt.bstrVal);
597 	events->adapter_desc = _wcsdup(vt.bstrVal);
598 	VariantClear(&vt);
599 
600 	/*
601 	 * Try to get even better candidate for matching with InstanceName
602 	 * from Win32_PnPEntity. This is needed at least for some USB cards
603 	 * that can change the InstanceName whenever being unplugged and
604 	 * plugged again.
605 	 */
606 
607 	hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
608 	if (!SUCCEEDED(hr)) {
609 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
610 			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
611 		IWbemClassObject_Release(pObj);
612 		IWbemServices_Release(pSvc);
613 		if (events->adapter_desc == NULL)
614 			return ndis_events_use_desc(events, desc);
615 		return 0; /* use Win32_NetworkAdapter::Name */
616 	}
617 
618 	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
619 		   "'%S'", vt.bstrVal);
620 
621 	len = _snwprintf(query, MAX_QUERY_LEN,
622 			L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
623 	if (len < 0 || len >= MAX_QUERY_LEN - 1) {
624 		VariantClear(&vt);
625 		IWbemClassObject_Release(pObj);
626 		IWbemServices_Release(pSvc);
627 		if (events->adapter_desc == NULL)
628 			return ndis_events_use_desc(events, desc);
629 		return 0; /* use Win32_NetworkAdapter::Name */
630 	}
631 
632 	/* Escape \ as \\ */
633 	for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
634 		if (vt.bstrVal[pos] == '\\') {
635 			if (len >= MAX_QUERY_LEN - 3)
636 				break;
637 			query[len++] = '\\';
638 		}
639 		query[len++] = vt.bstrVal[pos];
640 	}
641 	query[len++] = L'\'';
642 	query[len] = L'\0';
643 	VariantClear(&vt);
644 	IWbemClassObject_Release(pObj);
645 	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
646 
647 	hr = call_IWbemServices_ExecQuery(
648 		pSvc, L"WQL", query,
649 		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
650 		NULL, &pEnumerator);
651 	if (!SUCCEEDED(hr)) {
652 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
653 			   "Name from Win32_PnPEntity: 0x%x", (int) hr);
654 		IWbemServices_Release(pSvc);
655 		if (events->adapter_desc == NULL)
656 			return ndis_events_use_desc(events, desc);
657 		return 0; /* use Win32_NetworkAdapter::Name */
658 	}
659 
660 	uReturned = 0;
661 	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
662 				       &pObj, &uReturned);
663 	if (!SUCCEEDED(hr) || uReturned == 0) {
664 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
665 			   "from Win32_PnPEntity: 0x%x", (int) hr);
666 		IEnumWbemClassObject_Release(pEnumerator);
667 		IWbemServices_Release(pSvc);
668 		if (events->adapter_desc == NULL)
669 			return ndis_events_use_desc(events, desc);
670 		return 0; /* use Win32_NetworkAdapter::Name */
671 	}
672 	IEnumWbemClassObject_Release(pEnumerator);
673 
674 	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
675 	if (!SUCCEEDED(hr)) {
676 		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
677 			   "Win32_PnPEntity: 0x%x", (int) hr);
678 		IWbemClassObject_Release(pObj);
679 		IWbemServices_Release(pSvc);
680 		if (events->adapter_desc == NULL)
681 			return ndis_events_use_desc(events, desc);
682 		return 0; /* use Win32_NetworkAdapter::Name */
683 	}
684 
685 	wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
686 		   vt.bstrVal);
687 	os_free(events->adapter_desc);
688 	events->adapter_desc = _wcsdup(vt.bstrVal);
689 	VariantClear(&vt);
690 
691 	IWbemClassObject_Release(pObj);
692 
693 	IWbemServices_Release(pSvc);
694 
695 	if (events->adapter_desc == NULL)
696 		return ndis_events_use_desc(events, desc);
697 
698 	return 0;
699 }
700 
701 
702 struct ndis_events_data *
703 ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
704 		 const char *ifname, const char *desc)
705 {
706 	HRESULT hr;
707 	IWbemObjectSink *pSink;
708 	struct ndis_events_data *events;
709 
710 	events = os_zalloc(sizeof(*events));
711 	if (events == NULL) {
712 		wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
713 		return NULL;
714 	}
715 	events->ifname = os_strdup(ifname);
716 	if (events->ifname == NULL) {
717 		os_free(events);
718 		return NULL;
719 	}
720 
721 	if (wmi_refcnt++ == 0) {
722 		hr = CoInitializeEx(0, COINIT_MULTITHREADED);
723 		if (FAILED(hr)) {
724 			wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
725 				   "returned 0x%x", (int) hr);
726 			os_free(events);
727 			return NULL;
728 		}
729 	}
730 
731 	if (wmi_first) {
732 		/* CoInitializeSecurity() must be called once and only once
733 		 * per process, so let's use wmi_first flag to protect against
734 		 * multiple calls. */
735 		wmi_first = 0;
736 
737 		hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
738 					  RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
739 					  RPC_C_IMP_LEVEL_IMPERSONATE,
740 					  NULL, EOAC_SECURE_REFS, NULL);
741 		if (FAILED(hr)) {
742 			wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
743 				   "- returned 0x%x", (int) hr);
744 			os_free(events);
745 			return NULL;
746 		}
747 	}
748 
749 	hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
750 			      &IID_IWbemLocator,
751 			      (LPVOID *) (void *) &events->pLoc);
752 	if (FAILED(hr)) {
753 		wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
754 			   "0x%x", (int) hr);
755 		CoUninitialize();
756 		os_free(events);
757 		return NULL;
758 	}
759 
760 	if (ndis_events_get_adapter(events, ifname, desc) < 0) {
761 		CoUninitialize();
762 		os_free(events);
763 		return NULL;
764 	}
765 	wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
766 		   events->adapter_desc);
767 
768 	hr = call_IWbemLocator_ConnectServer(
769 		events->pLoc, L"ROOT\\WMI", NULL, NULL,
770 		0, 0, 0, 0, &events->pSvc);
771 	if (FAILED(hr)) {
772 		wpa_printf(MSG_ERROR, "Could not connect to server - error "
773 			   "0x%x", (int) hr);
774 		CoUninitialize();
775 		os_free(events->adapter_desc);
776 		os_free(events);
777 		return NULL;
778 	}
779 	wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
780 
781 	ndis_events_constructor(events);
782 	pSink = &events->sink;
783 	pSink->lpVtbl = &events->sink_vtbl;
784 	events->sink_vtbl.QueryInterface = ndis_events_query_interface;
785 	events->sink_vtbl.AddRef = ndis_events_add_ref;
786 	events->sink_vtbl.Release = ndis_events_release;
787 	events->sink_vtbl.Indicate = ndis_events_indicate;
788 	events->sink_vtbl.SetStatus = ndis_events_set_status;
789 
790 	if (register_async_notification(pSink, events->pSvc) < 0) {
791 		wpa_printf(MSG_DEBUG, "Failed to register async "
792 			   "notifications");
793 		ndis_events_destructor(events);
794 		os_free(events->adapter_desc);
795 		os_free(events);
796 		return NULL;
797 	}
798 
799 	*read_pipe = events->read_pipe;
800 	*event_avail = events->event_avail;
801 
802 	return events;
803 }
804