ndis_events.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
1/*
2 * ndis_events - Receive NdisMIndicateStatus() events using WMI
3 * Copyright (c) 2004-2006, Jouni Malinen <j@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
27static int wmi_refcnt = 0;
28static int wmi_first = 1;
29
30struct 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 */
49HRESULT 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
69HRESULT 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
90HRESULT 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
118enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
119		   EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
120
121static int ndis_events_get_adapter(struct ndis_events_data *events,
122				   const char *ifname, const char *desc);
123
124
125static 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
147static 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
159static HRESULT STDMETHODCALLTYPE
160ndis_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
175static 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
182static 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
198static 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
232static 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
239static 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
246static 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
294static 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
301static 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
308static HRESULT STDMETHODCALLTYPE
309ndis_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
392static HRESULT STDMETHODCALLTYPE
393ndis_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
400static 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
422static 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
444void 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
456static 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
489static 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
707struct ndis_events_data *
708ndis_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