Added WirelessManager, a port of wpa_supplicant.
[AROS.git] / workbench / network / WirelessManager / src / drivers / ndis_events.c
blobf6eaa7c9f7ee2734f351d53436b9d5970147e933
1 /*
2 * ndis_events - Receive NdisMIndicateStatus() events using WMI
3 * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #define _WIN32_WINNT 0x0400
17 #include "includes.h"
19 #ifndef COBJMACROS
20 #define COBJMACROS
21 #endif /* COBJMACROS */
22 #include <wbemidl.h>
24 #include "common.h"
27 static int wmi_refcnt = 0;
28 static int wmi_first = 1;
30 struct ndis_events_data {
31 IWbemObjectSink sink;
32 IWbemObjectSinkVtbl sink_vtbl;
34 IWbemServices *pSvc;
35 IWbemLocator *pLoc;
37 HANDLE read_pipe, write_pipe, event_avail;
38 UINT ref;
39 int terminating;
40 char *ifname; /* {GUID..} */
41 WCHAR *adapter_desc;
44 #define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
45 #define BstrFree(x) if (x) SysFreeString(x)
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)
53 BSTR bsQueryLanguage, bsQuery;
54 HRESULT hr;
56 bsQueryLanguage = BstrAlloc(strQueryLanguage);
57 bsQuery = BstrAlloc(strQuery);
59 hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
60 pCtx, ppEnum);
62 BstrFree(bsQueryLanguage);
63 BstrFree(bsQuery);
65 return hr;
69 HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
70 IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
71 long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
73 BSTR bsQueryLanguage, bsQuery;
74 HRESULT hr;
76 bsQueryLanguage = BstrAlloc(strQueryLanguage);
77 bsQuery = BstrAlloc(strQuery);
79 hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
80 bsQuery, lFlags, pCtx,
81 pResponseHandler);
83 BstrFree(bsQueryLanguage);
84 BstrFree(bsQuery);
86 return hr;
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)
95 BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
96 HRESULT hr;
98 bsNetworkResource = BstrAlloc(strNetworkResource);
99 bsUser = BstrAlloc(strUser);
100 bsPassword = BstrAlloc(strPassword);
101 bsLocale = BstrAlloc(strLocale);
102 bsAuthority = BstrAlloc(strAuthority);
104 hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
105 bsPassword, bsLocale, lSecurityFlags,
106 bsAuthority, pCtx, ppNamespace);
108 BstrFree(bsNetworkResource);
109 BstrFree(bsUser);
110 BstrFree(bsPassword);
111 BstrFree(bsLocale);
112 BstrFree(bsAuthority);
114 return hr;
118 enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
119 EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
121 static int ndis_events_get_adapter(struct ndis_events_data *events,
122 const char *ifname, const char *desc);
125 static int ndis_events_constructor(struct ndis_events_data *events)
127 events->ref = 1;
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;
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;
143 return 0;
147 static void ndis_events_destructor(struct ndis_events_data *events)
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();
159 static HRESULT STDMETHODCALLTYPE
160 ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
162 *obj = NULL;
164 if (IsEqualIID(riid, &IID_IUnknown) ||
165 IsEqualIID(riid, &IID_IWbemObjectSink)) {
166 *obj = this;
167 IWbemObjectSink_AddRef(this);
168 return NOERROR;
171 return E_NOINTERFACE;
175 static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
177 struct ndis_events_data *events = (struct ndis_events_data *) this;
178 return ++events->ref;
182 static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
184 struct ndis_events_data *events = (struct ndis_events_data *) this;
186 if (--events->ref != 0)
187 return events->ref;
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;
198 static int ndis_events_send_event(struct ndis_events_data *events,
199 enum event_types type,
200 char *data, size_t data_len)
202 char buf[512], *pos, *end;
203 int _type;
204 DWORD written;
206 end = buf + sizeof(buf);
207 _type = (int) type;
208 os_memcpy(buf, &_type, sizeof(_type));
209 pos = buf + sizeof(_type);
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;
217 *pos++ = data_len >> 8;
218 *pos++ = data_len & 0xff;
219 os_memcpy(pos, data, data_len);
220 pos += data_len;
223 if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
224 SetEvent(events->event_avail);
225 return 0;
227 wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
228 return -1;
232 static void ndis_events_media_connect(struct ndis_events_data *events)
234 wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
235 ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
239 static void ndis_events_media_disconnect(struct ndis_events_data *events)
241 wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
242 ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
246 static void ndis_events_media_specific(struct ndis_events_data *events,
247 IWbemClassObject *pObj)
249 VARIANT vt;
250 HRESULT hr;
251 LONG lower, upper, k;
252 UCHAR ch;
253 char *data, *pos;
254 size_t data_len;
256 wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
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;
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;
279 pos = data;
280 for (k = lower; k <= upper; k++) {
281 SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
282 *pos++ = ch;
284 wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
286 VariantClear(&vt);
288 ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
290 os_free(data);
294 static void ndis_events_adapter_arrival(struct ndis_events_data *events)
296 wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
297 ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
301 static void ndis_events_adapter_removal(struct ndis_events_data *events)
303 wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
304 ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
308 static HRESULT STDMETHODCALLTYPE
309 ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
310 IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
312 struct ndis_events_data *events = (struct ndis_events_data *) this;
313 long i;
315 if (events->terminating) {
316 wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
317 "indication - terminating");
318 return WBEM_NO_ERROR;
320 /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
321 lObjectCount); */
323 for (i = 0; i < lObjectCount; i++) {
324 IWbemClassObject *pObj = ppObjArray[i];
325 HRESULT hr;
326 VARIANT vtClass, vt;
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;
335 /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
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;
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);
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;
363 VariantClear(&vt);
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);
385 VariantClear(&vtClass);
388 return WBEM_NO_ERROR;
392 static HRESULT STDMETHODCALLTYPE
393 ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
394 BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
396 return WBEM_NO_ERROR;
400 static int notification_query(IWbemObjectSink *pDestSink,
401 IWbemServices *pSvc, const char *class_name)
403 HRESULT hr;
404 WCHAR query[256];
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;
418 return 0;
422 static int register_async_notification(IWbemObjectSink *pDestSink,
423 IWbemServices *pSvc)
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
435 for (i = 0; class_list[i]; i++) {
436 if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
437 return -1;
440 return 0;
444 void ndis_events_deinit(struct ndis_events_data *events)
446 events->terminating = 1;
447 IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
448 IWbemObjectSink_Release(&events->sink);
450 * Rest of deinitialization is done in ndis_events_destructor() once
451 * all reference count drops to zero.
456 static int ndis_events_use_desc(struct ndis_events_data *events,
457 const char *desc)
459 char *tmp, *pos;
460 size_t len;
462 if (desc == NULL) {
463 if (events->adapter_desc == NULL)
464 return -1;
465 /* Continue using old description */
466 return 0;
469 tmp = os_strdup(desc);
470 if (tmp == NULL)
471 return -1;
473 pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
474 if (pos)
475 *pos = '\0';
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;
483 _snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
484 os_free(tmp);
485 return 0;
489 static int ndis_events_get_adapter(struct ndis_events_data *events,
490 const char *ifname, const char *desc)
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;
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.
508 os_free(events->adapter_desc);
509 events->adapter_desc = NULL;
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);
518 wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
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);
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);
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);
548 IEnumWbemClassObject_Release(pEnumerator);
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);
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);
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);
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);
589 IEnumWbemClassObject_Release(pEnumerator);
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);
600 wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
601 vt.bstrVal);
602 events->adapter_desc = _wcsdup(vt.bstrVal);
603 VariantClear(&vt);
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.
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 */
623 wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
624 "'%S'", vt.bstrVal);
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 */
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++] = '\\';
644 query[len++] = vt.bstrVal[pos];
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);
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 */
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 */
677 IEnumWbemClassObject_Release(pEnumerator);
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 */
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);
696 IWbemClassObject_Release(pObj);
698 IWbemServices_Release(pSvc);
700 if (events->adapter_desc == NULL)
701 return ndis_events_use_desc(events, desc);
703 return 0;
707 struct ndis_events_data *
708 ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
709 const char *ifname, const char *desc)
711 HRESULT hr;
712 IWbemObjectSink *pSink;
713 struct ndis_events_data *events;
715 events = os_zalloc(sizeof(*events));
716 if (events == NULL) {
717 wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
718 return NULL;
720 events->ifname = os_strdup(ifname);
721 if (events->ifname == NULL) {
722 os_free(events);
723 return NULL;
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;
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;
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;
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;
765 if (ndis_events_get_adapter(events, ifname, desc) < 0) {
766 CoUninitialize();
767 os_free(events);
768 return NULL;
770 wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
771 events->adapter_desc);
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;
784 wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
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;
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;
804 *read_pipe = events->read_pipe;
805 *event_avail = events->event_avail;
807 return events;