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
21static int wmi_refcnt = 0;
22static int wmi_first = 1;
23
24struct 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 */
43HRESULT 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
63HRESULT 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
84HRESULT 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
112enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
113		   EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
114
115static int ndis_events_get_adapter(struct ndis_events_data *events,
116				   const char *ifname, const char *desc);
117
118
119static 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
141static 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
153static HRESULT STDMETHODCALLTYPE
154ndis_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
169static 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
176static 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
192static 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
226static 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
233static 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
240static 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
288static 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
295static 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
302static HRESULT STDMETHODCALLTYPE
303ndis_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
386static HRESULT STDMETHODCALLTYPE
387ndis_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
394static 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
416static 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
438void 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
450static 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
483static 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
701struct ndis_events_data *
702ndis_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