1 /* 2 * ndis_events - Receive NdisMIndicateStatus() events using WMI 3 * Copyright (c) 2004-2006, Jouni Malinen <j (at) 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", 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, (LPVOID *) &events->pLoc); 756 if (FAILED(hr)) { 757 wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " 758 "0x%x", (int) hr); 759 CoUninitialize(); 760 os_free(events); 761 return NULL; 762 } 763 764 if (ndis_events_get_adapter(events, ifname, desc) < 0) { 765 CoUninitialize(); 766 os_free(events); 767 return NULL; 768 } 769 wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", 770 events->adapter_desc); 771 772 hr = call_IWbemLocator_ConnectServer( 773 events->pLoc, L"ROOT\\WMI", NULL, NULL, 774 0, 0, 0, 0, &events->pSvc); 775 if (FAILED(hr)) { 776 wpa_printf(MSG_ERROR, "Could not connect to server - error " 777 "0x%x", (int) hr); 778 CoUninitialize(); 779 os_free(events->adapter_desc); 780 os_free(events); 781 return NULL; 782 } 783 wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); 784 785 ndis_events_constructor(events); 786 pSink = &events->sink; 787 pSink->lpVtbl = &events->sink_vtbl; 788 events->sink_vtbl.QueryInterface = ndis_events_query_interface; 789 events->sink_vtbl.AddRef = ndis_events_add_ref; 790 events->sink_vtbl.Release = ndis_events_release; 791 events->sink_vtbl.Indicate = ndis_events_indicate; 792 events->sink_vtbl.SetStatus = ndis_events_set_status; 793 794 if (register_async_notification(pSink, events->pSvc) < 0) { 795 wpa_printf(MSG_DEBUG, "Failed to register async " 796 "notifications"); 797 ndis_events_destructor(events); 798 os_free(events->adapter_desc); 799 os_free(events); 800 return NULL; 801 } 802 803 *read_pipe = events->read_pipe; 804 *event_avail = events->event_avail; 805 806 return events; 807 } 808