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 21 static int wmi_refcnt = 0; 22 static int wmi_first = 1; 23 24 struct 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 */ 43 HRESULT 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 63 HRESULT 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 84 HRESULT 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 112 enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, 113 EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; 114 115 static int ndis_events_get_adapter(struct ndis_events_data *events, 116 const char *ifname, const char *desc); 117 118 119 static 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 141 static 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 153 static HRESULT STDMETHODCALLTYPE 154 ndis_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 169 static 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 176 static 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 192 static 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 226 static 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 233 static 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 240 static 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 288 static 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 295 static 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 302 static HRESULT STDMETHODCALLTYPE 303 ndis_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, 376 "Unexpected event - __CLASS: '%S'", 377 vtClass.bstrVal); 378 } 379 380 VariantClear(&vtClass); 381 } 382 383 return WBEM_NO_ERROR; 384 } 385 386 387 static HRESULT STDMETHODCALLTYPE 388 ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult, 389 BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) 390 { 391 return WBEM_NO_ERROR; 392 } 393 394 395 static int notification_query(IWbemObjectSink *pDestSink, 396 IWbemServices *pSvc, const char *class_name) 397 { 398 HRESULT hr; 399 WCHAR query[256]; 400 401 _snwprintf(query, 256, 402 L"SELECT * FROM %S", class_name); 403 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 404 hr = call_IWbemServices_ExecNotificationQueryAsync( 405 pSvc, L"WQL", query, 0, 0, pDestSink); 406 if (FAILED(hr)) { 407 wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s " 408 "failed with hresult of 0x%x", 409 class_name, (int) hr); 410 return -1; 411 } 412 413 return 0; 414 } 415 416 417 static int register_async_notification(IWbemObjectSink *pDestSink, 418 IWbemServices *pSvc) 419 { 420 int i; 421 const char *class_list[] = { 422 "MSNdis_StatusMediaConnect", 423 "MSNdis_StatusMediaDisconnect", 424 "MSNdis_StatusMediaSpecificIndication", 425 "MSNdis_NotifyAdapterArrival", 426 "MSNdis_NotifyAdapterRemoval", 427 NULL 428 }; 429 430 for (i = 0; class_list[i]; i++) { 431 if (notification_query(pDestSink, pSvc, class_list[i]) < 0) 432 return -1; 433 } 434 435 return 0; 436 } 437 438 439 void ndis_events_deinit(struct ndis_events_data *events) 440 { 441 events->terminating = 1; 442 IWbemServices_CancelAsyncCall(events->pSvc, &events->sink); 443 IWbemObjectSink_Release(&events->sink); 444 /* 445 * Rest of deinitialization is done in ndis_events_destructor() once 446 * all reference count drops to zero. 447 */ 448 } 449 450 451 static int ndis_events_use_desc(struct ndis_events_data *events, 452 const char *desc) 453 { 454 char *tmp, *pos; 455 size_t len; 456 457 if (desc == NULL) { 458 if (events->adapter_desc == NULL) 459 return -1; 460 /* Continue using old description */ 461 return 0; 462 } 463 464 tmp = os_strdup(desc); 465 if (tmp == NULL) 466 return -1; 467 468 pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)"); 469 if (pos) 470 *pos = '\0'; 471 472 len = os_strlen(tmp); 473 events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR)); 474 if (events->adapter_desc == NULL) { 475 os_free(tmp); 476 return -1; 477 } 478 _snwprintf(events->adapter_desc, len + 1, L"%S", tmp); 479 os_free(tmp); 480 return 0; 481 } 482 483 484 static int ndis_events_get_adapter(struct ndis_events_data *events, 485 const char *ifname, const char *desc) 486 { 487 HRESULT hr; 488 IWbemServices *pSvc; 489 #define MAX_QUERY_LEN 256 490 WCHAR query[MAX_QUERY_LEN]; 491 IEnumWbemClassObject *pEnumerator; 492 IWbemClassObject *pObj; 493 ULONG uReturned; 494 VARIANT vt; 495 int len, pos; 496 497 /* 498 * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter 499 * to have better probability of matching with InstanceName from 500 * MSNdis events. If this fails, use the provided description. 501 */ 502 503 os_free(events->adapter_desc); 504 events->adapter_desc = NULL; 505 506 hr = call_IWbemLocator_ConnectServer( 507 events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc); 508 if (FAILED(hr)) { 509 wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI " 510 "server (ROOT\\CIMV2) - error 0x%x", (int) hr); 511 return ndis_events_use_desc(events, desc); 512 } 513 wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2."); 514 515 _snwprintf(query, MAX_QUERY_LEN, 516 L"SELECT Index FROM Win32_NetworkAdapterConfiguration " 517 L"WHERE SettingID='%S'", ifname); 518 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 519 520 hr = call_IWbemServices_ExecQuery( 521 pSvc, L"WQL", query, 522 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 523 NULL, &pEnumerator); 524 if (!SUCCEEDED(hr)) { 525 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " 526 "GUID from Win32_NetworkAdapterConfiguration: " 527 "0x%x", (int) hr); 528 IWbemServices_Release(pSvc); 529 return ndis_events_use_desc(events, desc); 530 } 531 532 uReturned = 0; 533 hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, 534 &pObj, &uReturned); 535 if (!SUCCEEDED(hr) || uReturned == 0) { 536 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " 537 "GUID from Win32_NetworkAdapterConfiguration: " 538 "0x%x", (int) hr); 539 IEnumWbemClassObject_Release(pEnumerator); 540 IWbemServices_Release(pSvc); 541 return ndis_events_use_desc(events, desc); 542 } 543 IEnumWbemClassObject_Release(pEnumerator); 544 545 VariantInit(&vt); 546 hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL); 547 if (!SUCCEEDED(hr)) { 548 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from " 549 "Win32_NetworkAdapterConfiguration: 0x%x", 550 (int) hr); 551 IWbemServices_Release(pSvc); 552 return ndis_events_use_desc(events, desc); 553 } 554 555 _snwprintf(query, MAX_QUERY_LEN, 556 L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE " 557 L"Index=%d", 558 vt.uintVal); 559 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 560 VariantClear(&vt); 561 IWbemClassObject_Release(pObj); 562 563 hr = call_IWbemServices_ExecQuery( 564 pSvc, L"WQL", query, 565 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 566 NULL, &pEnumerator); 567 if (!SUCCEEDED(hr)) { 568 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " 569 "from Win32_NetworkAdapter: 0x%x", (int) hr); 570 IWbemServices_Release(pSvc); 571 return ndis_events_use_desc(events, desc); 572 } 573 574 uReturned = 0; 575 hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, 576 &pObj, &uReturned); 577 if (!SUCCEEDED(hr) || uReturned == 0) { 578 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " 579 "from Win32_NetworkAdapter: 0x%x", (int) hr); 580 IEnumWbemClassObject_Release(pEnumerator); 581 IWbemServices_Release(pSvc); 582 return ndis_events_use_desc(events, desc); 583 } 584 IEnumWbemClassObject_Release(pEnumerator); 585 586 hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); 587 if (!SUCCEEDED(hr)) { 588 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " 589 "Win32_NetworkAdapter: 0x%x", (int) hr); 590 IWbemClassObject_Release(pObj); 591 IWbemServices_Release(pSvc); 592 return ndis_events_use_desc(events, desc); 593 } 594 595 wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'", 596 vt.bstrVal); 597 events->adapter_desc = _wcsdup(vt.bstrVal); 598 VariantClear(&vt); 599 600 /* 601 * Try to get even better candidate for matching with InstanceName 602 * from Win32_PnPEntity. This is needed at least for some USB cards 603 * that can change the InstanceName whenever being unplugged and 604 * plugged again. 605 */ 606 607 hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL); 608 if (!SUCCEEDED(hr)) { 609 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID " 610 "from Win32_NetworkAdapter: 0x%x", (int) hr); 611 IWbemClassObject_Release(pObj); 612 IWbemServices_Release(pSvc); 613 if (events->adapter_desc == NULL) 614 return ndis_events_use_desc(events, desc); 615 return 0; /* use Win32_NetworkAdapter::Name */ 616 } 617 618 wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID=" 619 "'%S'", vt.bstrVal); 620 621 len = _snwprintf(query, MAX_QUERY_LEN, 622 L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='"); 623 if (len < 0 || len >= MAX_QUERY_LEN - 1) { 624 VariantClear(&vt); 625 IWbemClassObject_Release(pObj); 626 IWbemServices_Release(pSvc); 627 if (events->adapter_desc == NULL) 628 return ndis_events_use_desc(events, desc); 629 return 0; /* use Win32_NetworkAdapter::Name */ 630 } 631 632 /* Escape \ as \\ */ 633 for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) { 634 if (vt.bstrVal[pos] == '\\') { 635 if (len >= MAX_QUERY_LEN - 3) 636 break; 637 query[len++] = '\\'; 638 } 639 query[len++] = vt.bstrVal[pos]; 640 } 641 query[len++] = L'\''; 642 query[len] = L'\0'; 643 VariantClear(&vt); 644 IWbemClassObject_Release(pObj); 645 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 646 647 hr = call_IWbemServices_ExecQuery( 648 pSvc, L"WQL", query, 649 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 650 NULL, &pEnumerator); 651 if (!SUCCEEDED(hr)) { 652 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " 653 "Name from Win32_PnPEntity: 0x%x", (int) hr); 654 IWbemServices_Release(pSvc); 655 if (events->adapter_desc == NULL) 656 return ndis_events_use_desc(events, desc); 657 return 0; /* use Win32_NetworkAdapter::Name */ 658 } 659 660 uReturned = 0; 661 hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, 662 &pObj, &uReturned); 663 if (!SUCCEEDED(hr) || uReturned == 0) { 664 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " 665 "from Win32_PnPEntity: 0x%x", (int) hr); 666 IEnumWbemClassObject_Release(pEnumerator); 667 IWbemServices_Release(pSvc); 668 if (events->adapter_desc == NULL) 669 return ndis_events_use_desc(events, desc); 670 return 0; /* use Win32_NetworkAdapter::Name */ 671 } 672 IEnumWbemClassObject_Release(pEnumerator); 673 674 hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); 675 if (!SUCCEEDED(hr)) { 676 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " 677 "Win32_PnPEntity: 0x%x", (int) hr); 678 IWbemClassObject_Release(pObj); 679 IWbemServices_Release(pSvc); 680 if (events->adapter_desc == NULL) 681 return ndis_events_use_desc(events, desc); 682 return 0; /* use Win32_NetworkAdapter::Name */ 683 } 684 685 wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'", 686 vt.bstrVal); 687 os_free(events->adapter_desc); 688 events->adapter_desc = _wcsdup(vt.bstrVal); 689 VariantClear(&vt); 690 691 IWbemClassObject_Release(pObj); 692 693 IWbemServices_Release(pSvc); 694 695 if (events->adapter_desc == NULL) 696 return ndis_events_use_desc(events, desc); 697 698 return 0; 699 } 700 701 702 struct ndis_events_data * 703 ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail, 704 const char *ifname, const char *desc) 705 { 706 HRESULT hr; 707 IWbemObjectSink *pSink; 708 struct ndis_events_data *events; 709 710 events = os_zalloc(sizeof(*events)); 711 if (events == NULL) { 712 wpa_printf(MSG_ERROR, "Could not allocate sink for events."); 713 return NULL; 714 } 715 events->ifname = os_strdup(ifname); 716 if (events->ifname == NULL) { 717 os_free(events); 718 return NULL; 719 } 720 721 if (wmi_refcnt++ == 0) { 722 hr = CoInitializeEx(0, COINIT_MULTITHREADED); 723 if (FAILED(hr)) { 724 wpa_printf(MSG_ERROR, "CoInitializeEx() failed - " 725 "returned 0x%x", (int) hr); 726 os_free(events); 727 return NULL; 728 } 729 } 730 731 if (wmi_first) { 732 /* CoInitializeSecurity() must be called once and only once 733 * per process, so let's use wmi_first flag to protect against 734 * multiple calls. */ 735 wmi_first = 0; 736 737 hr = CoInitializeSecurity(NULL, -1, NULL, NULL, 738 RPC_C_AUTHN_LEVEL_PKT_PRIVACY, 739 RPC_C_IMP_LEVEL_IMPERSONATE, 740 NULL, EOAC_SECURE_REFS, NULL); 741 if (FAILED(hr)) { 742 wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed " 743 "- returned 0x%x", (int) hr); 744 os_free(events); 745 return NULL; 746 } 747 } 748 749 hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, 750 &IID_IWbemLocator, 751 (LPVOID *) (void *) &events->pLoc); 752 if (FAILED(hr)) { 753 wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " 754 "0x%x", (int) hr); 755 CoUninitialize(); 756 os_free(events); 757 return NULL; 758 } 759 760 if (ndis_events_get_adapter(events, ifname, desc) < 0) { 761 CoUninitialize(); 762 os_free(events); 763 return NULL; 764 } 765 wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", 766 events->adapter_desc); 767 768 hr = call_IWbemLocator_ConnectServer( 769 events->pLoc, L"ROOT\\WMI", NULL, NULL, 770 0, 0, 0, 0, &events->pSvc); 771 if (FAILED(hr)) { 772 wpa_printf(MSG_ERROR, "Could not connect to server - error " 773 "0x%x", (int) hr); 774 CoUninitialize(); 775 os_free(events->adapter_desc); 776 os_free(events); 777 return NULL; 778 } 779 wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); 780 781 ndis_events_constructor(events); 782 pSink = &events->sink; 783 pSink->lpVtbl = &events->sink_vtbl; 784 events->sink_vtbl.QueryInterface = ndis_events_query_interface; 785 events->sink_vtbl.AddRef = ndis_events_add_ref; 786 events->sink_vtbl.Release = ndis_events_release; 787 events->sink_vtbl.Indicate = ndis_events_indicate; 788 events->sink_vtbl.SetStatus = ndis_events_set_status; 789 790 if (register_async_notification(pSink, events->pSvc) < 0) { 791 wpa_printf(MSG_DEBUG, "Failed to register async " 792 "notifications"); 793 ndis_events_destructor(events); 794 os_free(events->adapter_desc); 795 os_free(events); 796 return NULL; 797 } 798 799 *read_pipe = events->read_pipe; 800 *event_avail = events->event_avail; 801 802 return events; 803 } 804