1209139Srpaulo/*
2209139Srpaulo * ndis_events - Receive NdisMIndicateStatus() events using WMI
3209139Srpaulo * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
4209139Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7209139Srpaulo */
8209139Srpaulo
9209139Srpaulo#define _WIN32_WINNT    0x0400
10209139Srpaulo
11209139Srpaulo#include "includes.h"
12209139Srpaulo
13209139Srpaulo#ifndef COBJMACROS
14209139Srpaulo#define COBJMACROS
15209139Srpaulo#endif /* COBJMACROS */
16209139Srpaulo#include <wbemidl.h>
17209139Srpaulo
18209139Srpaulo#include "common.h"
19209139Srpaulo
20209139Srpaulo
21209139Srpaulostatic int wmi_refcnt = 0;
22209139Srpaulostatic int wmi_first = 1;
23209139Srpaulo
24209139Srpaulostruct ndis_events_data {
25209139Srpaulo	IWbemObjectSink sink;
26209139Srpaulo	IWbemObjectSinkVtbl sink_vtbl;
27209139Srpaulo
28209139Srpaulo	IWbemServices *pSvc;
29209139Srpaulo	IWbemLocator *pLoc;
30209139Srpaulo
31209139Srpaulo	HANDLE read_pipe, write_pipe, event_avail;
32209139Srpaulo	UINT ref;
33209139Srpaulo	int terminating;
34209139Srpaulo	char *ifname; /* {GUID..} */
35209139Srpaulo	WCHAR *adapter_desc;
36209139Srpaulo};
37209139Srpaulo
38209139Srpaulo#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
39209139Srpaulo#define BstrFree(x) if (x) SysFreeString(x)
40209139Srpaulo
41209139Srpaulo/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
42209139Srpaulo * BSTRs */
43209139SrpauloHRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
44209139Srpaulo	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
45209139Srpaulo	long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
46209139Srpaulo{
47209139Srpaulo	BSTR bsQueryLanguage, bsQuery;
48209139Srpaulo	HRESULT hr;
49209139Srpaulo
50209139Srpaulo	bsQueryLanguage = BstrAlloc(strQueryLanguage);
51209139Srpaulo	bsQuery = BstrAlloc(strQuery);
52209139Srpaulo
53209139Srpaulo	hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
54209139Srpaulo				     pCtx, ppEnum);
55209139Srpaulo
56209139Srpaulo	BstrFree(bsQueryLanguage);
57209139Srpaulo	BstrFree(bsQuery);
58209139Srpaulo
59209139Srpaulo	return hr;
60209139Srpaulo}
61209139Srpaulo
62209139Srpaulo
63209139SrpauloHRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
64209139Srpaulo	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
65209139Srpaulo	long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
66209139Srpaulo{
67209139Srpaulo	BSTR bsQueryLanguage, bsQuery;
68209139Srpaulo	HRESULT hr;
69209139Srpaulo
70209139Srpaulo	bsQueryLanguage = BstrAlloc(strQueryLanguage);
71209139Srpaulo	bsQuery = BstrAlloc(strQuery);
72209139Srpaulo
73209139Srpaulo	hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
74209139Srpaulo						      bsQuery, lFlags, pCtx,
75209139Srpaulo						      pResponseHandler);
76209139Srpaulo
77209139Srpaulo	BstrFree(bsQueryLanguage);
78209139Srpaulo	BstrFree(bsQuery);
79209139Srpaulo
80209139Srpaulo	return hr;
81209139Srpaulo}
82209139Srpaulo
83209139Srpaulo
84209139SrpauloHRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
85209139Srpaulo	IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
86209139Srpaulo	LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
87209139Srpaulo	LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
88209139Srpaulo{
89209139Srpaulo	BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
90209139Srpaulo	HRESULT hr;
91209139Srpaulo
92209139Srpaulo	bsNetworkResource = BstrAlloc(strNetworkResource);
93209139Srpaulo	bsUser = BstrAlloc(strUser);
94209139Srpaulo	bsPassword = BstrAlloc(strPassword);
95209139Srpaulo	bsLocale = BstrAlloc(strLocale);
96209139Srpaulo	bsAuthority = BstrAlloc(strAuthority);
97209139Srpaulo
98209139Srpaulo	hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
99209139Srpaulo					bsPassword, bsLocale, lSecurityFlags,
100209139Srpaulo					bsAuthority, pCtx, ppNamespace);
101209139Srpaulo
102209139Srpaulo	BstrFree(bsNetworkResource);
103209139Srpaulo	BstrFree(bsUser);
104209139Srpaulo	BstrFree(bsPassword);
105209139Srpaulo	BstrFree(bsLocale);
106209139Srpaulo	BstrFree(bsAuthority);
107209139Srpaulo
108209139Srpaulo	return hr;
109209139Srpaulo}
110209139Srpaulo
111209139Srpaulo
112209139Srpauloenum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
113209139Srpaulo		   EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
114209139Srpaulo
115209139Srpaulostatic int ndis_events_get_adapter(struct ndis_events_data *events,
116209139Srpaulo				   const char *ifname, const char *desc);
117209139Srpaulo
118209139Srpaulo
119209139Srpaulostatic int ndis_events_constructor(struct ndis_events_data *events)
120209139Srpaulo{
121209139Srpaulo	events->ref = 1;
122209139Srpaulo
123209139Srpaulo	if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
124209139Srpaulo		wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
125209139Srpaulo			   (int) GetLastError());
126209139Srpaulo		return -1;
127209139Srpaulo	}
128209139Srpaulo	events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
129209139Srpaulo	if (events->event_avail == NULL) {
130209139Srpaulo		wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
131209139Srpaulo			   (int) GetLastError());
132209139Srpaulo		CloseHandle(events->read_pipe);
133209139Srpaulo		CloseHandle(events->write_pipe);
134209139Srpaulo		return -1;
135209139Srpaulo	}
136209139Srpaulo
137209139Srpaulo	return 0;
138209139Srpaulo}
139209139Srpaulo
140209139Srpaulo
141209139Srpaulostatic void ndis_events_destructor(struct ndis_events_data *events)
142209139Srpaulo{
143209139Srpaulo	CloseHandle(events->read_pipe);
144209139Srpaulo	CloseHandle(events->write_pipe);
145209139Srpaulo	CloseHandle(events->event_avail);
146209139Srpaulo	IWbemServices_Release(events->pSvc);
147209139Srpaulo	IWbemLocator_Release(events->pLoc);
148209139Srpaulo	if (--wmi_refcnt == 0)
149209139Srpaulo		CoUninitialize();
150209139Srpaulo}
151209139Srpaulo
152209139Srpaulo
153209139Srpaulostatic HRESULT STDMETHODCALLTYPE
154209139Srpaulondis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
155209139Srpaulo{
156209139Srpaulo	*obj = NULL;
157209139Srpaulo
158209139Srpaulo	if (IsEqualIID(riid, &IID_IUnknown) ||
159209139Srpaulo	    IsEqualIID(riid, &IID_IWbemObjectSink)) {
160209139Srpaulo		*obj = this;
161209139Srpaulo		IWbemObjectSink_AddRef(this);
162209139Srpaulo		return NOERROR;
163209139Srpaulo	}
164209139Srpaulo
165209139Srpaulo	return E_NOINTERFACE;
166209139Srpaulo}
167209139Srpaulo
168209139Srpaulo
169209139Srpaulostatic ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
170209139Srpaulo{
171209139Srpaulo	struct ndis_events_data *events = (struct ndis_events_data *) this;
172209139Srpaulo	return ++events->ref;
173209139Srpaulo}
174209139Srpaulo
175209139Srpaulo
176209139Srpaulostatic ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
177209139Srpaulo{
178209139Srpaulo	struct ndis_events_data *events = (struct ndis_events_data *) this;
179209139Srpaulo
180209139Srpaulo	if (--events->ref != 0)
181209139Srpaulo		return events->ref;
182209139Srpaulo
183209139Srpaulo	ndis_events_destructor(events);
184209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: terminated");
185209139Srpaulo	os_free(events->adapter_desc);
186209139Srpaulo	os_free(events->ifname);
187209139Srpaulo	os_free(events);
188209139Srpaulo	return 0;
189209139Srpaulo}
190209139Srpaulo
191209139Srpaulo
192209139Srpaulostatic int ndis_events_send_event(struct ndis_events_data *events,
193209139Srpaulo				  enum event_types type,
194209139Srpaulo				  char *data, size_t data_len)
195209139Srpaulo{
196209139Srpaulo	char buf[512], *pos, *end;
197209139Srpaulo	int _type;
198209139Srpaulo	DWORD written;
199209139Srpaulo
200209139Srpaulo	end = buf + sizeof(buf);
201209139Srpaulo	_type = (int) type;
202209139Srpaulo	os_memcpy(buf, &_type, sizeof(_type));
203209139Srpaulo	pos = buf + sizeof(_type);
204209139Srpaulo
205209139Srpaulo	if (data) {
206209139Srpaulo		if (2 + data_len > (size_t) (end - pos)) {
207209139Srpaulo			wpa_printf(MSG_DEBUG, "Not enough room for send_event "
208209139Srpaulo				   "data (%d)", data_len);
209209139Srpaulo			return -1;
210209139Srpaulo		}
211209139Srpaulo		*pos++ = data_len >> 8;
212209139Srpaulo		*pos++ = data_len & 0xff;
213209139Srpaulo		os_memcpy(pos, data, data_len);
214209139Srpaulo		pos += data_len;
215209139Srpaulo	}
216209139Srpaulo
217209139Srpaulo	if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
218209139Srpaulo		SetEvent(events->event_avail);
219209139Srpaulo		return 0;
220209139Srpaulo	}
221209139Srpaulo	wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
222209139Srpaulo	return -1;
223209139Srpaulo}
224209139Srpaulo
225209139Srpaulo
226209139Srpaulostatic void ndis_events_media_connect(struct ndis_events_data *events)
227209139Srpaulo{
228209139Srpaulo	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
229209139Srpaulo	ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
230209139Srpaulo}
231209139Srpaulo
232209139Srpaulo
233209139Srpaulostatic void ndis_events_media_disconnect(struct ndis_events_data *events)
234209139Srpaulo{
235209139Srpaulo	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
236209139Srpaulo	ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
237209139Srpaulo}
238209139Srpaulo
239209139Srpaulo
240209139Srpaulostatic void ndis_events_media_specific(struct ndis_events_data *events,
241209139Srpaulo				       IWbemClassObject *pObj)
242209139Srpaulo{
243209139Srpaulo	VARIANT vt;
244209139Srpaulo	HRESULT hr;
245209139Srpaulo	LONG lower, upper, k;
246209139Srpaulo	UCHAR ch;
247209139Srpaulo	char *data, *pos;
248209139Srpaulo	size_t data_len;
249209139Srpaulo
250209139Srpaulo	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
251209139Srpaulo
252209139Srpaulo	/* This is the StatusBuffer from NdisMIndicateStatus() call */
253209139Srpaulo	hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
254209139Srpaulo				  0, &vt, NULL, NULL);
255209139Srpaulo	if (FAILED(hr)) {
256209139Srpaulo		wpa_printf(MSG_DEBUG, "Could not get "
257209139Srpaulo			   "NdisStatusMediaSpecificIndication from "
258209139Srpaulo			   "the object?!");
259209139Srpaulo		return;
260209139Srpaulo	}
261209139Srpaulo
262209139Srpaulo	SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
263209139Srpaulo	SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
264209139Srpaulo	data_len = upper - lower + 1;
265209139Srpaulo	data = os_malloc(data_len);
266209139Srpaulo	if (data == NULL) {
267209139Srpaulo		wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
268209139Srpaulo			   "data");
269209139Srpaulo		VariantClear(&vt);
270209139Srpaulo		return;
271209139Srpaulo	}
272209139Srpaulo
273209139Srpaulo	pos = data;
274209139Srpaulo	for (k = lower; k <= upper; k++) {
275209139Srpaulo		SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
276209139Srpaulo		*pos++ = ch;
277209139Srpaulo	}
278209139Srpaulo	wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
279209139Srpaulo
280209139Srpaulo	VariantClear(&vt);
281209139Srpaulo
282209139Srpaulo	ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
283209139Srpaulo
284209139Srpaulo	os_free(data);
285209139Srpaulo}
286209139Srpaulo
287209139Srpaulo
288209139Srpaulostatic void ndis_events_adapter_arrival(struct ndis_events_data *events)
289209139Srpaulo{
290209139Srpaulo	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
291209139Srpaulo	ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
292209139Srpaulo}
293209139Srpaulo
294209139Srpaulo
295209139Srpaulostatic void ndis_events_adapter_removal(struct ndis_events_data *events)
296209139Srpaulo{
297209139Srpaulo	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
298209139Srpaulo	ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
299209139Srpaulo}
300209139Srpaulo
301209139Srpaulo
302209139Srpaulostatic HRESULT STDMETHODCALLTYPE
303209139Srpaulondis_events_indicate(IWbemObjectSink *this, long lObjectCount,
304209139Srpaulo		     IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
305209139Srpaulo{
306209139Srpaulo	struct ndis_events_data *events = (struct ndis_events_data *) this;
307209139Srpaulo	long i;
308209139Srpaulo
309209139Srpaulo	if (events->terminating) {
310209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
311209139Srpaulo			   "indication - terminating");
312209139Srpaulo		return WBEM_NO_ERROR;
313209139Srpaulo	}
314209139Srpaulo	/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
315209139Srpaulo	   lObjectCount); */
316209139Srpaulo
317209139Srpaulo	for (i = 0; i < lObjectCount; i++) {
318209139Srpaulo		IWbemClassObject *pObj = ppObjArray[i];
319209139Srpaulo		HRESULT hr;
320209139Srpaulo		VARIANT vtClass, vt;
321209139Srpaulo
322209139Srpaulo		hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
323209139Srpaulo					  NULL);
324209139Srpaulo		if (FAILED(hr)) {
325209139Srpaulo			wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
326209139Srpaulo				   "event.");
327209139Srpaulo			break;
328209139Srpaulo		}
329209139Srpaulo		/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
330209139Srpaulo
331209139Srpaulo		hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
332209139Srpaulo					  NULL);
333209139Srpaulo		if (FAILED(hr)) {
334209139Srpaulo			wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
335209139Srpaulo				   "from event.");
336209139Srpaulo			VariantClear(&vtClass);
337209139Srpaulo			break;
338209139Srpaulo		}
339209139Srpaulo
340209139Srpaulo		if (wcscmp(vtClass.bstrVal,
341209139Srpaulo			   L"MSNdis_NotifyAdapterArrival") == 0) {
342209139Srpaulo			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
343209139Srpaulo				   "update adapter description since it may "
344209139Srpaulo				   "have changed with new adapter instance");
345209139Srpaulo			ndis_events_get_adapter(events, events->ifname, NULL);
346209139Srpaulo		}
347209139Srpaulo
348209139Srpaulo		if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
349209139Srpaulo			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
350209139Srpaulo				   "indication for foreign adapter: "
351209139Srpaulo				   "InstanceName: '%S' __CLASS: '%S'",
352209139Srpaulo				   vt.bstrVal, vtClass.bstrVal);
353209139Srpaulo			VariantClear(&vtClass);
354209139Srpaulo			VariantClear(&vt);
355209139Srpaulo			continue;
356209139Srpaulo		}
357209139Srpaulo		VariantClear(&vt);
358209139Srpaulo
359209139Srpaulo		if (wcscmp(vtClass.bstrVal,
360209139Srpaulo			   L"MSNdis_StatusMediaSpecificIndication") == 0) {
361209139Srpaulo			ndis_events_media_specific(events, pObj);
362209139Srpaulo		} else if (wcscmp(vtClass.bstrVal,
363209139Srpaulo				  L"MSNdis_StatusMediaConnect") == 0) {
364209139Srpaulo			ndis_events_media_connect(events);
365209139Srpaulo		} else if (wcscmp(vtClass.bstrVal,
366209139Srpaulo				  L"MSNdis_StatusMediaDisconnect") == 0) {
367209139Srpaulo			ndis_events_media_disconnect(events);
368209139Srpaulo		} else if (wcscmp(vtClass.bstrVal,
369209139Srpaulo				  L"MSNdis_NotifyAdapterArrival") == 0) {
370209139Srpaulo			ndis_events_adapter_arrival(events);
371209139Srpaulo		} else if (wcscmp(vtClass.bstrVal,
372209139Srpaulo				  L"MSNdis_NotifyAdapterRemoval") == 0) {
373209139Srpaulo			ndis_events_adapter_removal(events);
374209139Srpaulo		} else {
375209139Srpaulo			wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
376209139Srpaulo				   "'%S'", vtClass.bstrVal);
377209139Srpaulo		}
378209139Srpaulo
379209139Srpaulo		VariantClear(&vtClass);
380209139Srpaulo	}
381209139Srpaulo
382209139Srpaulo	return WBEM_NO_ERROR;
383209139Srpaulo}
384209139Srpaulo
385209139Srpaulo
386209139Srpaulostatic HRESULT STDMETHODCALLTYPE
387209139Srpaulondis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
388209139Srpaulo		       BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
389209139Srpaulo{
390209139Srpaulo	return WBEM_NO_ERROR;
391209139Srpaulo}
392209139Srpaulo
393209139Srpaulo
394209139Srpaulostatic int notification_query(IWbemObjectSink *pDestSink,
395209139Srpaulo			      IWbemServices *pSvc, const char *class_name)
396209139Srpaulo{
397209139Srpaulo	HRESULT hr;
398209139Srpaulo	WCHAR query[256];
399209139Srpaulo
400209139Srpaulo	_snwprintf(query, 256,
401209139Srpaulo		  L"SELECT * FROM %S", class_name);
402209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
403209139Srpaulo	hr = call_IWbemServices_ExecNotificationQueryAsync(
404209139Srpaulo		pSvc, L"WQL", query, 0, 0, pDestSink);
405209139Srpaulo	if (FAILED(hr)) {
406209139Srpaulo		wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
407209139Srpaulo			   "failed with hresult of 0x%x",
408209139Srpaulo			   class_name, (int) hr);
409209139Srpaulo		return -1;
410209139Srpaulo	}
411209139Srpaulo
412209139Srpaulo	return 0;
413209139Srpaulo}
414209139Srpaulo
415209139Srpaulo
416209139Srpaulostatic int register_async_notification(IWbemObjectSink *pDestSink,
417209139Srpaulo				       IWbemServices *pSvc)
418209139Srpaulo{
419209139Srpaulo	int i;
420209139Srpaulo	const char *class_list[] = {
421209139Srpaulo		"MSNdis_StatusMediaConnect",
422209139Srpaulo		"MSNdis_StatusMediaDisconnect",
423209139Srpaulo		"MSNdis_StatusMediaSpecificIndication",
424209139Srpaulo		"MSNdis_NotifyAdapterArrival",
425209139Srpaulo		"MSNdis_NotifyAdapterRemoval",
426209139Srpaulo		NULL
427209139Srpaulo	};
428209139Srpaulo
429209139Srpaulo	for (i = 0; class_list[i]; i++) {
430209139Srpaulo		if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
431209139Srpaulo			return -1;
432209139Srpaulo	}
433209139Srpaulo
434209139Srpaulo	return 0;
435209139Srpaulo}
436209139Srpaulo
437209139Srpaulo
438209139Srpaulovoid ndis_events_deinit(struct ndis_events_data *events)
439209139Srpaulo{
440209139Srpaulo	events->terminating = 1;
441209139Srpaulo	IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
442209139Srpaulo	IWbemObjectSink_Release(&events->sink);
443209139Srpaulo	/*
444209139Srpaulo	 * Rest of deinitialization is done in ndis_events_destructor() once
445209139Srpaulo	 * all reference count drops to zero.
446209139Srpaulo	 */
447209139Srpaulo}
448209139Srpaulo
449209139Srpaulo
450209139Srpaulostatic int ndis_events_use_desc(struct ndis_events_data *events,
451209139Srpaulo				const char *desc)
452209139Srpaulo{
453209139Srpaulo	char *tmp, *pos;
454209139Srpaulo	size_t len;
455209139Srpaulo
456209139Srpaulo	if (desc == NULL) {
457209139Srpaulo		if (events->adapter_desc == NULL)
458209139Srpaulo			return -1;
459209139Srpaulo		/* Continue using old description */
460209139Srpaulo		return 0;
461209139Srpaulo	}
462209139Srpaulo
463209139Srpaulo	tmp = os_strdup(desc);
464209139Srpaulo	if (tmp == NULL)
465209139Srpaulo		return -1;
466209139Srpaulo
467209139Srpaulo	pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
468209139Srpaulo	if (pos)
469209139Srpaulo		*pos = '\0';
470209139Srpaulo
471209139Srpaulo	len = os_strlen(tmp);
472209139Srpaulo	events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
473209139Srpaulo	if (events->adapter_desc == NULL) {
474209139Srpaulo		os_free(tmp);
475209139Srpaulo		return -1;
476209139Srpaulo	}
477209139Srpaulo	_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
478209139Srpaulo	os_free(tmp);
479209139Srpaulo	return 0;
480209139Srpaulo}
481209139Srpaulo
482209139Srpaulo
483209139Srpaulostatic int ndis_events_get_adapter(struct ndis_events_data *events,
484209139Srpaulo				   const char *ifname, const char *desc)
485209139Srpaulo{
486209139Srpaulo	HRESULT hr;
487209139Srpaulo	IWbemServices *pSvc;
488209139Srpaulo#define MAX_QUERY_LEN 256
489209139Srpaulo	WCHAR query[MAX_QUERY_LEN];
490209139Srpaulo	IEnumWbemClassObject *pEnumerator;
491209139Srpaulo	IWbemClassObject *pObj;
492209139Srpaulo	ULONG uReturned;
493209139Srpaulo	VARIANT vt;
494209139Srpaulo	int len, pos;
495209139Srpaulo
496209139Srpaulo	/*
497209139Srpaulo	 * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
498209139Srpaulo	 * to have better probability of matching with InstanceName from
499209139Srpaulo	 * MSNdis events. If this fails, use the provided description.
500209139Srpaulo	 */
501209139Srpaulo
502209139Srpaulo	os_free(events->adapter_desc);
503209139Srpaulo	events->adapter_desc = NULL;
504209139Srpaulo
505209139Srpaulo	hr = call_IWbemLocator_ConnectServer(
506209139Srpaulo		events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
507209139Srpaulo	if (FAILED(hr)) {
508209139Srpaulo		wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
509209139Srpaulo			   "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
510209139Srpaulo		return ndis_events_use_desc(events, desc);
511209139Srpaulo	}
512209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
513209139Srpaulo
514209139Srpaulo	_snwprintf(query, MAX_QUERY_LEN,
515209139Srpaulo		  L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
516209139Srpaulo		  L"WHERE SettingID='%S'", ifname);
517209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
518209139Srpaulo
519209139Srpaulo	hr = call_IWbemServices_ExecQuery(
520209139Srpaulo		pSvc, L"WQL", query,
521209139Srpaulo		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
522209139Srpaulo		NULL, &pEnumerator);
523209139Srpaulo	if (!SUCCEEDED(hr)) {
524209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
525209139Srpaulo			   "GUID from Win32_NetworkAdapterConfiguration: "
526209139Srpaulo			   "0x%x", (int) hr);
527209139Srpaulo		IWbemServices_Release(pSvc);
528209139Srpaulo		return ndis_events_use_desc(events, desc);
529209139Srpaulo	}
530209139Srpaulo
531209139Srpaulo	uReturned = 0;
532209139Srpaulo	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
533209139Srpaulo				       &pObj, &uReturned);
534209139Srpaulo	if (!SUCCEEDED(hr) || uReturned == 0) {
535209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
536209139Srpaulo			   "GUID from Win32_NetworkAdapterConfiguration: "
537209139Srpaulo			   "0x%x", (int) hr);
538209139Srpaulo		IEnumWbemClassObject_Release(pEnumerator);
539209139Srpaulo		IWbemServices_Release(pSvc);
540209139Srpaulo		return ndis_events_use_desc(events, desc);
541209139Srpaulo	}
542209139Srpaulo	IEnumWbemClassObject_Release(pEnumerator);
543209139Srpaulo
544209139Srpaulo	VariantInit(&vt);
545209139Srpaulo	hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
546209139Srpaulo	if (!SUCCEEDED(hr)) {
547209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
548209139Srpaulo			   "Win32_NetworkAdapterConfiguration: 0x%x",
549209139Srpaulo			   (int) hr);
550209139Srpaulo		IWbemServices_Release(pSvc);
551209139Srpaulo		return ndis_events_use_desc(events, desc);
552209139Srpaulo	}
553209139Srpaulo
554209139Srpaulo	_snwprintf(query, MAX_QUERY_LEN,
555209139Srpaulo		  L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
556209139Srpaulo		  L"Index=%d",
557209139Srpaulo		  vt.uintVal);
558209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
559209139Srpaulo	VariantClear(&vt);
560209139Srpaulo	IWbemClassObject_Release(pObj);
561209139Srpaulo
562209139Srpaulo	hr = call_IWbemServices_ExecQuery(
563209139Srpaulo		pSvc, L"WQL", query,
564209139Srpaulo		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
565209139Srpaulo		NULL, &pEnumerator);
566209139Srpaulo	if (!SUCCEEDED(hr)) {
567209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
568209139Srpaulo			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
569209139Srpaulo		IWbemServices_Release(pSvc);
570209139Srpaulo		return ndis_events_use_desc(events, desc);
571209139Srpaulo	}
572209139Srpaulo
573209139Srpaulo	uReturned = 0;
574209139Srpaulo	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
575209139Srpaulo				       &pObj, &uReturned);
576209139Srpaulo	if (!SUCCEEDED(hr) || uReturned == 0) {
577209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
578209139Srpaulo			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
579209139Srpaulo		IEnumWbemClassObject_Release(pEnumerator);
580209139Srpaulo		IWbemServices_Release(pSvc);
581209139Srpaulo		return ndis_events_use_desc(events, desc);
582209139Srpaulo	}
583209139Srpaulo	IEnumWbemClassObject_Release(pEnumerator);
584209139Srpaulo
585209139Srpaulo	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
586209139Srpaulo	if (!SUCCEEDED(hr)) {
587209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
588209139Srpaulo			   "Win32_NetworkAdapter: 0x%x", (int) hr);
589209139Srpaulo		IWbemClassObject_Release(pObj);
590209139Srpaulo		IWbemServices_Release(pSvc);
591209139Srpaulo		return ndis_events_use_desc(events, desc);
592209139Srpaulo	}
593209139Srpaulo
594209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
595209139Srpaulo		   vt.bstrVal);
596209139Srpaulo	events->adapter_desc = _wcsdup(vt.bstrVal);
597209139Srpaulo	VariantClear(&vt);
598209139Srpaulo
599209139Srpaulo	/*
600209139Srpaulo	 * Try to get even better candidate for matching with InstanceName
601209139Srpaulo	 * from Win32_PnPEntity. This is needed at least for some USB cards
602209139Srpaulo	 * that can change the InstanceName whenever being unplugged and
603209139Srpaulo	 * plugged again.
604209139Srpaulo	 */
605209139Srpaulo
606209139Srpaulo	hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
607209139Srpaulo	if (!SUCCEEDED(hr)) {
608209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
609209139Srpaulo			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
610209139Srpaulo		IWbemClassObject_Release(pObj);
611209139Srpaulo		IWbemServices_Release(pSvc);
612209139Srpaulo		if (events->adapter_desc == NULL)
613209139Srpaulo			return ndis_events_use_desc(events, desc);
614209139Srpaulo		return 0; /* use Win32_NetworkAdapter::Name */
615209139Srpaulo	}
616209139Srpaulo
617209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
618209139Srpaulo		   "'%S'", vt.bstrVal);
619209139Srpaulo
620209139Srpaulo	len = _snwprintf(query, MAX_QUERY_LEN,
621209139Srpaulo			L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
622209139Srpaulo	if (len < 0 || len >= MAX_QUERY_LEN - 1) {
623209139Srpaulo		VariantClear(&vt);
624209139Srpaulo		IWbemClassObject_Release(pObj);
625209139Srpaulo		IWbemServices_Release(pSvc);
626209139Srpaulo		if (events->adapter_desc == NULL)
627209139Srpaulo			return ndis_events_use_desc(events, desc);
628209139Srpaulo		return 0; /* use Win32_NetworkAdapter::Name */
629209139Srpaulo	}
630209139Srpaulo
631209139Srpaulo	/* Escape \ as \\ */
632209139Srpaulo	for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
633209139Srpaulo		if (vt.bstrVal[pos] == '\\') {
634209139Srpaulo			if (len >= MAX_QUERY_LEN - 3)
635209139Srpaulo				break;
636209139Srpaulo			query[len++] = '\\';
637209139Srpaulo		}
638209139Srpaulo		query[len++] = vt.bstrVal[pos];
639209139Srpaulo	}
640209139Srpaulo	query[len++] = L'\'';
641209139Srpaulo	query[len] = L'\0';
642209139Srpaulo	VariantClear(&vt);
643209139Srpaulo	IWbemClassObject_Release(pObj);
644209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
645209139Srpaulo
646209139Srpaulo	hr = call_IWbemServices_ExecQuery(
647209139Srpaulo		pSvc, L"WQL", query,
648209139Srpaulo		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
649209139Srpaulo		NULL, &pEnumerator);
650209139Srpaulo	if (!SUCCEEDED(hr)) {
651209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
652209139Srpaulo			   "Name from Win32_PnPEntity: 0x%x", (int) hr);
653209139Srpaulo		IWbemServices_Release(pSvc);
654209139Srpaulo		if (events->adapter_desc == NULL)
655209139Srpaulo			return ndis_events_use_desc(events, desc);
656209139Srpaulo		return 0; /* use Win32_NetworkAdapter::Name */
657209139Srpaulo	}
658209139Srpaulo
659209139Srpaulo	uReturned = 0;
660209139Srpaulo	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
661209139Srpaulo				       &pObj, &uReturned);
662209139Srpaulo	if (!SUCCEEDED(hr) || uReturned == 0) {
663209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
664209139Srpaulo			   "from Win32_PnPEntity: 0x%x", (int) hr);
665209139Srpaulo		IEnumWbemClassObject_Release(pEnumerator);
666209139Srpaulo		IWbemServices_Release(pSvc);
667209139Srpaulo		if (events->adapter_desc == NULL)
668209139Srpaulo			return ndis_events_use_desc(events, desc);
669209139Srpaulo		return 0; /* use Win32_NetworkAdapter::Name */
670209139Srpaulo	}
671209139Srpaulo	IEnumWbemClassObject_Release(pEnumerator);
672209139Srpaulo
673209139Srpaulo	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
674209139Srpaulo	if (!SUCCEEDED(hr)) {
675209139Srpaulo		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
676209139Srpaulo			   "Win32_PnPEntity: 0x%x", (int) hr);
677209139Srpaulo		IWbemClassObject_Release(pObj);
678209139Srpaulo		IWbemServices_Release(pSvc);
679209139Srpaulo		if (events->adapter_desc == NULL)
680209139Srpaulo			return ndis_events_use_desc(events, desc);
681209139Srpaulo		return 0; /* use Win32_NetworkAdapter::Name */
682209139Srpaulo	}
683209139Srpaulo
684209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
685209139Srpaulo		   vt.bstrVal);
686209139Srpaulo	os_free(events->adapter_desc);
687209139Srpaulo	events->adapter_desc = _wcsdup(vt.bstrVal);
688209139Srpaulo	VariantClear(&vt);
689209139Srpaulo
690209139Srpaulo	IWbemClassObject_Release(pObj);
691209139Srpaulo
692209139Srpaulo	IWbemServices_Release(pSvc);
693209139Srpaulo
694209139Srpaulo	if (events->adapter_desc == NULL)
695209139Srpaulo		return ndis_events_use_desc(events, desc);
696209139Srpaulo
697209139Srpaulo	return 0;
698209139Srpaulo}
699209139Srpaulo
700209139Srpaulo
701209139Srpaulostruct ndis_events_data *
702209139Srpaulondis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
703209139Srpaulo		 const char *ifname, const char *desc)
704209139Srpaulo{
705209139Srpaulo	HRESULT hr;
706209139Srpaulo	IWbemObjectSink *pSink;
707209139Srpaulo	struct ndis_events_data *events;
708209139Srpaulo
709209139Srpaulo	events = os_zalloc(sizeof(*events));
710209139Srpaulo	if (events == NULL) {
711209139Srpaulo		wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
712209139Srpaulo		return NULL;
713209139Srpaulo	}
714209139Srpaulo	events->ifname = os_strdup(ifname);
715209139Srpaulo	if (events->ifname == NULL) {
716209139Srpaulo		os_free(events);
717209139Srpaulo		return NULL;
718209139Srpaulo	}
719209139Srpaulo
720209139Srpaulo	if (wmi_refcnt++ == 0) {
721209139Srpaulo		hr = CoInitializeEx(0, COINIT_MULTITHREADED);
722209139Srpaulo		if (FAILED(hr)) {
723209139Srpaulo			wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
724209139Srpaulo				   "returned 0x%x", (int) hr);
725209139Srpaulo			os_free(events);
726209139Srpaulo			return NULL;
727209139Srpaulo		}
728209139Srpaulo	}
729209139Srpaulo
730209139Srpaulo	if (wmi_first) {
731209139Srpaulo		/* CoInitializeSecurity() must be called once and only once
732209139Srpaulo		 * per process, so let's use wmi_first flag to protect against
733209139Srpaulo		 * multiple calls. */
734209139Srpaulo		wmi_first = 0;
735209139Srpaulo
736209139Srpaulo		hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
737209139Srpaulo					  RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
738209139Srpaulo					  RPC_C_IMP_LEVEL_IMPERSONATE,
739209139Srpaulo					  NULL, EOAC_SECURE_REFS, NULL);
740209139Srpaulo		if (FAILED(hr)) {
741209139Srpaulo			wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
742209139Srpaulo				   "- returned 0x%x", (int) hr);
743209139Srpaulo			os_free(events);
744209139Srpaulo			return NULL;
745209139Srpaulo		}
746209139Srpaulo	}
747209139Srpaulo
748209139Srpaulo	hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
749209139Srpaulo			      &IID_IWbemLocator,
750209139Srpaulo			      (LPVOID *) (void *) &events->pLoc);
751209139Srpaulo	if (FAILED(hr)) {
752209139Srpaulo		wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
753209139Srpaulo			   "0x%x", (int) hr);
754209139Srpaulo		CoUninitialize();
755209139Srpaulo		os_free(events);
756209139Srpaulo		return NULL;
757209139Srpaulo	}
758209139Srpaulo
759209139Srpaulo	if (ndis_events_get_adapter(events, ifname, desc) < 0) {
760209139Srpaulo		CoUninitialize();
761209139Srpaulo		os_free(events);
762209139Srpaulo		return NULL;
763209139Srpaulo	}
764209139Srpaulo	wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
765209139Srpaulo		   events->adapter_desc);
766209139Srpaulo
767209139Srpaulo	hr = call_IWbemLocator_ConnectServer(
768209139Srpaulo		events->pLoc, L"ROOT\\WMI", NULL, NULL,
769209139Srpaulo		0, 0, 0, 0, &events->pSvc);
770209139Srpaulo	if (FAILED(hr)) {
771209139Srpaulo		wpa_printf(MSG_ERROR, "Could not connect to server - error "
772209139Srpaulo			   "0x%x", (int) hr);
773209139Srpaulo		CoUninitialize();
774209139Srpaulo		os_free(events->adapter_desc);
775209139Srpaulo		os_free(events);
776209139Srpaulo		return NULL;
777209139Srpaulo	}
778209139Srpaulo	wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
779209139Srpaulo
780209139Srpaulo	ndis_events_constructor(events);
781209139Srpaulo	pSink = &events->sink;
782209139Srpaulo	pSink->lpVtbl = &events->sink_vtbl;
783209139Srpaulo	events->sink_vtbl.QueryInterface = ndis_events_query_interface;
784209139Srpaulo	events->sink_vtbl.AddRef = ndis_events_add_ref;
785209139Srpaulo	events->sink_vtbl.Release = ndis_events_release;
786209139Srpaulo	events->sink_vtbl.Indicate = ndis_events_indicate;
787209139Srpaulo	events->sink_vtbl.SetStatus = ndis_events_set_status;
788209139Srpaulo
789209139Srpaulo	if (register_async_notification(pSink, events->pSvc) < 0) {
790209139Srpaulo		wpa_printf(MSG_DEBUG, "Failed to register async "
791209139Srpaulo			   "notifications");
792209139Srpaulo		ndis_events_destructor(events);
793209139Srpaulo		os_free(events->adapter_desc);
794209139Srpaulo		os_free(events);
795209139Srpaulo		return NULL;
796209139Srpaulo	}
797209139Srpaulo
798209139Srpaulo	*read_pipe = events->read_pipe;
799209139Srpaulo	*event_avail = events->event_avail;
800209139Srpaulo
801209139Srpaulo	return events;
802209139Srpaulo}
803