xref: /freebsd/contrib/wpa/src/drivers/ndis_events.c (revision 3157ba21)
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 
27 static int wmi_refcnt = 0;
28 static int wmi_first = 1;
29 
30 struct 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 */
49 HRESULT 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 
69 HRESULT 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 
90 HRESULT 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 
118 enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
119 		   EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
120 
121 static int ndis_events_get_adapter(struct ndis_events_data *events,
122 				   const char *ifname, const char *desc);
123 
124 
125 static 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 
147 static 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 
159 static HRESULT STDMETHODCALLTYPE
160 ndis_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 
175 static 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 
182 static 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 
198 static 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 
232 static 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 
239 static 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 
246 static 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 
294 static 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 
301 static 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 
308 static HRESULT STDMETHODCALLTYPE
309 ndis_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 
392 static HRESULT STDMETHODCALLTYPE
393 ndis_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 
400 static 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 
422 static 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 
444 void 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 
456 static 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 
489 static 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 
707 struct ndis_events_data *
708 ndis_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