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