1209139Srpaulo/* 2209139Srpaulo * ndis_events - Receive NdisMIndicateStatus() events using WMI 3209139Srpaulo * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> 4209139Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7209139Srpaulo */ 8209139Srpaulo 9209139Srpaulo#define _WIN32_WINNT 0x0400 10209139Srpaulo 11209139Srpaulo#include "includes.h" 12209139Srpaulo 13209139Srpaulo#ifndef COBJMACROS 14209139Srpaulo#define COBJMACROS 15209139Srpaulo#endif /* COBJMACROS */ 16209139Srpaulo#include <wbemidl.h> 17209139Srpaulo 18209139Srpaulo#include "common.h" 19209139Srpaulo 20209139Srpaulo 21209139Srpaulostatic int wmi_refcnt = 0; 22209139Srpaulostatic int wmi_first = 1; 23209139Srpaulo 24209139Srpaulostruct ndis_events_data { 25209139Srpaulo IWbemObjectSink sink; 26209139Srpaulo IWbemObjectSinkVtbl sink_vtbl; 27209139Srpaulo 28209139Srpaulo IWbemServices *pSvc; 29209139Srpaulo IWbemLocator *pLoc; 30209139Srpaulo 31209139Srpaulo HANDLE read_pipe, write_pipe, event_avail; 32209139Srpaulo UINT ref; 33209139Srpaulo int terminating; 34209139Srpaulo char *ifname; /* {GUID..} */ 35209139Srpaulo WCHAR *adapter_desc; 36209139Srpaulo}; 37209139Srpaulo 38209139Srpaulo#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL 39209139Srpaulo#define BstrFree(x) if (x) SysFreeString(x) 40209139Srpaulo 41209139Srpaulo/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to 42209139Srpaulo * BSTRs */ 43209139SrpauloHRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery( 44209139Srpaulo IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, 45209139Srpaulo long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum) 46209139Srpaulo{ 47209139Srpaulo BSTR bsQueryLanguage, bsQuery; 48209139Srpaulo HRESULT hr; 49209139Srpaulo 50209139Srpaulo bsQueryLanguage = BstrAlloc(strQueryLanguage); 51209139Srpaulo bsQuery = BstrAlloc(strQuery); 52209139Srpaulo 53209139Srpaulo hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags, 54209139Srpaulo pCtx, ppEnum); 55209139Srpaulo 56209139Srpaulo BstrFree(bsQueryLanguage); 57209139Srpaulo BstrFree(bsQuery); 58209139Srpaulo 59209139Srpaulo return hr; 60209139Srpaulo} 61209139Srpaulo 62209139Srpaulo 63209139SrpauloHRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync( 64209139Srpaulo IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, 65209139Srpaulo long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) 66209139Srpaulo{ 67209139Srpaulo BSTR bsQueryLanguage, bsQuery; 68209139Srpaulo HRESULT hr; 69209139Srpaulo 70209139Srpaulo bsQueryLanguage = BstrAlloc(strQueryLanguage); 71209139Srpaulo bsQuery = BstrAlloc(strQuery); 72209139Srpaulo 73209139Srpaulo hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage, 74209139Srpaulo bsQuery, lFlags, pCtx, 75209139Srpaulo pResponseHandler); 76209139Srpaulo 77209139Srpaulo BstrFree(bsQueryLanguage); 78209139Srpaulo BstrFree(bsQuery); 79209139Srpaulo 80209139Srpaulo return hr; 81209139Srpaulo} 82209139Srpaulo 83209139Srpaulo 84209139SrpauloHRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer( 85209139Srpaulo IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser, 86209139Srpaulo LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags, 87209139Srpaulo LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace) 88209139Srpaulo{ 89209139Srpaulo BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority; 90209139Srpaulo HRESULT hr; 91209139Srpaulo 92209139Srpaulo bsNetworkResource = BstrAlloc(strNetworkResource); 93209139Srpaulo bsUser = BstrAlloc(strUser); 94209139Srpaulo bsPassword = BstrAlloc(strPassword); 95209139Srpaulo bsLocale = BstrAlloc(strLocale); 96209139Srpaulo bsAuthority = BstrAlloc(strAuthority); 97209139Srpaulo 98209139Srpaulo hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser, 99209139Srpaulo bsPassword, bsLocale, lSecurityFlags, 100209139Srpaulo bsAuthority, pCtx, ppNamespace); 101209139Srpaulo 102209139Srpaulo BstrFree(bsNetworkResource); 103209139Srpaulo BstrFree(bsUser); 104209139Srpaulo BstrFree(bsPassword); 105209139Srpaulo BstrFree(bsLocale); 106209139Srpaulo BstrFree(bsAuthority); 107209139Srpaulo 108209139Srpaulo return hr; 109209139Srpaulo} 110209139Srpaulo 111209139Srpaulo 112209139Srpauloenum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, 113209139Srpaulo EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; 114209139Srpaulo 115209139Srpaulostatic int ndis_events_get_adapter(struct ndis_events_data *events, 116209139Srpaulo const char *ifname, const char *desc); 117209139Srpaulo 118209139Srpaulo 119209139Srpaulostatic int ndis_events_constructor(struct ndis_events_data *events) 120209139Srpaulo{ 121209139Srpaulo events->ref = 1; 122209139Srpaulo 123209139Srpaulo if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) { 124209139Srpaulo wpa_printf(MSG_ERROR, "CreatePipe() failed: %d", 125209139Srpaulo (int) GetLastError()); 126209139Srpaulo return -1; 127209139Srpaulo } 128209139Srpaulo events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL); 129209139Srpaulo if (events->event_avail == NULL) { 130209139Srpaulo wpa_printf(MSG_ERROR, "CreateEvent() failed: %d", 131209139Srpaulo (int) GetLastError()); 132209139Srpaulo CloseHandle(events->read_pipe); 133209139Srpaulo CloseHandle(events->write_pipe); 134209139Srpaulo return -1; 135209139Srpaulo } 136209139Srpaulo 137209139Srpaulo return 0; 138209139Srpaulo} 139209139Srpaulo 140209139Srpaulo 141209139Srpaulostatic void ndis_events_destructor(struct ndis_events_data *events) 142209139Srpaulo{ 143209139Srpaulo CloseHandle(events->read_pipe); 144209139Srpaulo CloseHandle(events->write_pipe); 145209139Srpaulo CloseHandle(events->event_avail); 146209139Srpaulo IWbemServices_Release(events->pSvc); 147209139Srpaulo IWbemLocator_Release(events->pLoc); 148209139Srpaulo if (--wmi_refcnt == 0) 149209139Srpaulo CoUninitialize(); 150209139Srpaulo} 151209139Srpaulo 152209139Srpaulo 153209139Srpaulostatic HRESULT STDMETHODCALLTYPE 154209139Srpaulondis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj) 155209139Srpaulo{ 156209139Srpaulo *obj = NULL; 157209139Srpaulo 158209139Srpaulo if (IsEqualIID(riid, &IID_IUnknown) || 159209139Srpaulo IsEqualIID(riid, &IID_IWbemObjectSink)) { 160209139Srpaulo *obj = this; 161209139Srpaulo IWbemObjectSink_AddRef(this); 162209139Srpaulo return NOERROR; 163209139Srpaulo } 164209139Srpaulo 165209139Srpaulo return E_NOINTERFACE; 166209139Srpaulo} 167209139Srpaulo 168209139Srpaulo 169209139Srpaulostatic ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this) 170209139Srpaulo{ 171209139Srpaulo struct ndis_events_data *events = (struct ndis_events_data *) this; 172209139Srpaulo return ++events->ref; 173209139Srpaulo} 174209139Srpaulo 175209139Srpaulo 176209139Srpaulostatic ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this) 177209139Srpaulo{ 178209139Srpaulo struct ndis_events_data *events = (struct ndis_events_data *) this; 179209139Srpaulo 180209139Srpaulo if (--events->ref != 0) 181209139Srpaulo return events->ref; 182209139Srpaulo 183209139Srpaulo ndis_events_destructor(events); 184209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: terminated"); 185209139Srpaulo os_free(events->adapter_desc); 186209139Srpaulo os_free(events->ifname); 187209139Srpaulo os_free(events); 188209139Srpaulo return 0; 189209139Srpaulo} 190209139Srpaulo 191209139Srpaulo 192209139Srpaulostatic int ndis_events_send_event(struct ndis_events_data *events, 193209139Srpaulo enum event_types type, 194209139Srpaulo char *data, size_t data_len) 195209139Srpaulo{ 196209139Srpaulo char buf[512], *pos, *end; 197209139Srpaulo int _type; 198209139Srpaulo DWORD written; 199209139Srpaulo 200209139Srpaulo end = buf + sizeof(buf); 201209139Srpaulo _type = (int) type; 202209139Srpaulo os_memcpy(buf, &_type, sizeof(_type)); 203209139Srpaulo pos = buf + sizeof(_type); 204209139Srpaulo 205209139Srpaulo if (data) { 206209139Srpaulo if (2 + data_len > (size_t) (end - pos)) { 207209139Srpaulo wpa_printf(MSG_DEBUG, "Not enough room for send_event " 208209139Srpaulo "data (%d)", data_len); 209209139Srpaulo return -1; 210209139Srpaulo } 211209139Srpaulo *pos++ = data_len >> 8; 212209139Srpaulo *pos++ = data_len & 0xff; 213209139Srpaulo os_memcpy(pos, data, data_len); 214209139Srpaulo pos += data_len; 215209139Srpaulo } 216209139Srpaulo 217209139Srpaulo if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) { 218209139Srpaulo SetEvent(events->event_avail); 219209139Srpaulo return 0; 220209139Srpaulo } 221209139Srpaulo wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError()); 222209139Srpaulo return -1; 223209139Srpaulo} 224209139Srpaulo 225209139Srpaulo 226209139Srpaulostatic void ndis_events_media_connect(struct ndis_events_data *events) 227209139Srpaulo{ 228209139Srpaulo wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect"); 229209139Srpaulo ndis_events_send_event(events, EVENT_CONNECT, NULL, 0); 230209139Srpaulo} 231209139Srpaulo 232209139Srpaulo 233209139Srpaulostatic void ndis_events_media_disconnect(struct ndis_events_data *events) 234209139Srpaulo{ 235209139Srpaulo wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect"); 236209139Srpaulo ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0); 237209139Srpaulo} 238209139Srpaulo 239209139Srpaulo 240209139Srpaulostatic void ndis_events_media_specific(struct ndis_events_data *events, 241209139Srpaulo IWbemClassObject *pObj) 242209139Srpaulo{ 243209139Srpaulo VARIANT vt; 244209139Srpaulo HRESULT hr; 245209139Srpaulo LONG lower, upper, k; 246209139Srpaulo UCHAR ch; 247209139Srpaulo char *data, *pos; 248209139Srpaulo size_t data_len; 249209139Srpaulo 250209139Srpaulo wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication"); 251209139Srpaulo 252209139Srpaulo /* This is the StatusBuffer from NdisMIndicateStatus() call */ 253209139Srpaulo hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication", 254209139Srpaulo 0, &vt, NULL, NULL); 255209139Srpaulo if (FAILED(hr)) { 256209139Srpaulo wpa_printf(MSG_DEBUG, "Could not get " 257209139Srpaulo "NdisStatusMediaSpecificIndication from " 258209139Srpaulo "the object?!"); 259209139Srpaulo return; 260209139Srpaulo } 261209139Srpaulo 262209139Srpaulo SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower); 263209139Srpaulo SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper); 264209139Srpaulo data_len = upper - lower + 1; 265209139Srpaulo data = os_malloc(data_len); 266209139Srpaulo if (data == NULL) { 267209139Srpaulo wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event " 268209139Srpaulo "data"); 269209139Srpaulo VariantClear(&vt); 270209139Srpaulo return; 271209139Srpaulo } 272209139Srpaulo 273209139Srpaulo pos = data; 274209139Srpaulo for (k = lower; k <= upper; k++) { 275209139Srpaulo SafeArrayGetElement(V_ARRAY(&vt), &k, &ch); 276209139Srpaulo *pos++ = ch; 277209139Srpaulo } 278209139Srpaulo wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len); 279209139Srpaulo 280209139Srpaulo VariantClear(&vt); 281209139Srpaulo 282209139Srpaulo ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len); 283209139Srpaulo 284209139Srpaulo os_free(data); 285209139Srpaulo} 286209139Srpaulo 287209139Srpaulo 288209139Srpaulostatic void ndis_events_adapter_arrival(struct ndis_events_data *events) 289209139Srpaulo{ 290209139Srpaulo wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival"); 291209139Srpaulo ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0); 292209139Srpaulo} 293209139Srpaulo 294209139Srpaulo 295209139Srpaulostatic void ndis_events_adapter_removal(struct ndis_events_data *events) 296209139Srpaulo{ 297209139Srpaulo wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval"); 298209139Srpaulo ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0); 299209139Srpaulo} 300209139Srpaulo 301209139Srpaulo 302209139Srpaulostatic HRESULT STDMETHODCALLTYPE 303209139Srpaulondis_events_indicate(IWbemObjectSink *this, long lObjectCount, 304209139Srpaulo IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray) 305209139Srpaulo{ 306209139Srpaulo struct ndis_events_data *events = (struct ndis_events_data *) this; 307209139Srpaulo long i; 308209139Srpaulo 309209139Srpaulo if (events->terminating) { 310209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " 311209139Srpaulo "indication - terminating"); 312209139Srpaulo return WBEM_NO_ERROR; 313209139Srpaulo } 314209139Srpaulo /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)", 315209139Srpaulo lObjectCount); */ 316209139Srpaulo 317209139Srpaulo for (i = 0; i < lObjectCount; i++) { 318209139Srpaulo IWbemClassObject *pObj = ppObjArray[i]; 319209139Srpaulo HRESULT hr; 320209139Srpaulo VARIANT vtClass, vt; 321209139Srpaulo 322209139Srpaulo hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL, 323209139Srpaulo NULL); 324209139Srpaulo if (FAILED(hr)) { 325209139Srpaulo wpa_printf(MSG_DEBUG, "Failed to get __CLASS from " 326209139Srpaulo "event."); 327209139Srpaulo break; 328209139Srpaulo } 329209139Srpaulo /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */ 330209139Srpaulo 331209139Srpaulo hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL, 332209139Srpaulo NULL); 333209139Srpaulo if (FAILED(hr)) { 334209139Srpaulo wpa_printf(MSG_DEBUG, "Failed to get InstanceName " 335209139Srpaulo "from event."); 336209139Srpaulo VariantClear(&vtClass); 337209139Srpaulo break; 338209139Srpaulo } 339209139Srpaulo 340209139Srpaulo if (wcscmp(vtClass.bstrVal, 341209139Srpaulo L"MSNdis_NotifyAdapterArrival") == 0) { 342209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to " 343209139Srpaulo "update adapter description since it may " 344209139Srpaulo "have changed with new adapter instance"); 345209139Srpaulo ndis_events_get_adapter(events, events->ifname, NULL); 346209139Srpaulo } 347209139Srpaulo 348209139Srpaulo if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) { 349209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " 350209139Srpaulo "indication for foreign adapter: " 351209139Srpaulo "InstanceName: '%S' __CLASS: '%S'", 352209139Srpaulo vt.bstrVal, vtClass.bstrVal); 353209139Srpaulo VariantClear(&vtClass); 354209139Srpaulo VariantClear(&vt); 355209139Srpaulo continue; 356209139Srpaulo } 357209139Srpaulo VariantClear(&vt); 358209139Srpaulo 359209139Srpaulo if (wcscmp(vtClass.bstrVal, 360209139Srpaulo L"MSNdis_StatusMediaSpecificIndication") == 0) { 361209139Srpaulo ndis_events_media_specific(events, pObj); 362209139Srpaulo } else if (wcscmp(vtClass.bstrVal, 363209139Srpaulo L"MSNdis_StatusMediaConnect") == 0) { 364209139Srpaulo ndis_events_media_connect(events); 365209139Srpaulo } else if (wcscmp(vtClass.bstrVal, 366209139Srpaulo L"MSNdis_StatusMediaDisconnect") == 0) { 367209139Srpaulo ndis_events_media_disconnect(events); 368209139Srpaulo } else if (wcscmp(vtClass.bstrVal, 369209139Srpaulo L"MSNdis_NotifyAdapterArrival") == 0) { 370209139Srpaulo ndis_events_adapter_arrival(events); 371209139Srpaulo } else if (wcscmp(vtClass.bstrVal, 372209139Srpaulo L"MSNdis_NotifyAdapterRemoval") == 0) { 373209139Srpaulo ndis_events_adapter_removal(events); 374209139Srpaulo } else { 375209139Srpaulo wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: " 376209139Srpaulo "'%S'", vtClass.bstrVal); 377209139Srpaulo } 378209139Srpaulo 379209139Srpaulo VariantClear(&vtClass); 380209139Srpaulo } 381209139Srpaulo 382209139Srpaulo return WBEM_NO_ERROR; 383209139Srpaulo} 384209139Srpaulo 385209139Srpaulo 386209139Srpaulostatic HRESULT STDMETHODCALLTYPE 387209139Srpaulondis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult, 388209139Srpaulo BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) 389209139Srpaulo{ 390209139Srpaulo return WBEM_NO_ERROR; 391209139Srpaulo} 392209139Srpaulo 393209139Srpaulo 394209139Srpaulostatic int notification_query(IWbemObjectSink *pDestSink, 395209139Srpaulo IWbemServices *pSvc, const char *class_name) 396209139Srpaulo{ 397209139Srpaulo HRESULT hr; 398209139Srpaulo WCHAR query[256]; 399209139Srpaulo 400209139Srpaulo _snwprintf(query, 256, 401209139Srpaulo L"SELECT * FROM %S", class_name); 402209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 403209139Srpaulo hr = call_IWbemServices_ExecNotificationQueryAsync( 404209139Srpaulo pSvc, L"WQL", query, 0, 0, pDestSink); 405209139Srpaulo if (FAILED(hr)) { 406209139Srpaulo wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s " 407209139Srpaulo "failed with hresult of 0x%x", 408209139Srpaulo class_name, (int) hr); 409209139Srpaulo return -1; 410209139Srpaulo } 411209139Srpaulo 412209139Srpaulo return 0; 413209139Srpaulo} 414209139Srpaulo 415209139Srpaulo 416209139Srpaulostatic int register_async_notification(IWbemObjectSink *pDestSink, 417209139Srpaulo IWbemServices *pSvc) 418209139Srpaulo{ 419209139Srpaulo int i; 420209139Srpaulo const char *class_list[] = { 421209139Srpaulo "MSNdis_StatusMediaConnect", 422209139Srpaulo "MSNdis_StatusMediaDisconnect", 423209139Srpaulo "MSNdis_StatusMediaSpecificIndication", 424209139Srpaulo "MSNdis_NotifyAdapterArrival", 425209139Srpaulo "MSNdis_NotifyAdapterRemoval", 426209139Srpaulo NULL 427209139Srpaulo }; 428209139Srpaulo 429209139Srpaulo for (i = 0; class_list[i]; i++) { 430209139Srpaulo if (notification_query(pDestSink, pSvc, class_list[i]) < 0) 431209139Srpaulo return -1; 432209139Srpaulo } 433209139Srpaulo 434209139Srpaulo return 0; 435209139Srpaulo} 436209139Srpaulo 437209139Srpaulo 438209139Srpaulovoid ndis_events_deinit(struct ndis_events_data *events) 439209139Srpaulo{ 440209139Srpaulo events->terminating = 1; 441209139Srpaulo IWbemServices_CancelAsyncCall(events->pSvc, &events->sink); 442209139Srpaulo IWbemObjectSink_Release(&events->sink); 443209139Srpaulo /* 444209139Srpaulo * Rest of deinitialization is done in ndis_events_destructor() once 445209139Srpaulo * all reference count drops to zero. 446209139Srpaulo */ 447209139Srpaulo} 448209139Srpaulo 449209139Srpaulo 450209139Srpaulostatic int ndis_events_use_desc(struct ndis_events_data *events, 451209139Srpaulo const char *desc) 452209139Srpaulo{ 453209139Srpaulo char *tmp, *pos; 454209139Srpaulo size_t len; 455209139Srpaulo 456209139Srpaulo if (desc == NULL) { 457209139Srpaulo if (events->adapter_desc == NULL) 458209139Srpaulo return -1; 459209139Srpaulo /* Continue using old description */ 460209139Srpaulo return 0; 461209139Srpaulo } 462209139Srpaulo 463209139Srpaulo tmp = os_strdup(desc); 464209139Srpaulo if (tmp == NULL) 465209139Srpaulo return -1; 466209139Srpaulo 467209139Srpaulo pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)"); 468209139Srpaulo if (pos) 469209139Srpaulo *pos = '\0'; 470209139Srpaulo 471209139Srpaulo len = os_strlen(tmp); 472209139Srpaulo events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR)); 473209139Srpaulo if (events->adapter_desc == NULL) { 474209139Srpaulo os_free(tmp); 475209139Srpaulo return -1; 476209139Srpaulo } 477209139Srpaulo _snwprintf(events->adapter_desc, len + 1, L"%S", tmp); 478209139Srpaulo os_free(tmp); 479209139Srpaulo return 0; 480209139Srpaulo} 481209139Srpaulo 482209139Srpaulo 483209139Srpaulostatic int ndis_events_get_adapter(struct ndis_events_data *events, 484209139Srpaulo const char *ifname, const char *desc) 485209139Srpaulo{ 486209139Srpaulo HRESULT hr; 487209139Srpaulo IWbemServices *pSvc; 488209139Srpaulo#define MAX_QUERY_LEN 256 489209139Srpaulo WCHAR query[MAX_QUERY_LEN]; 490209139Srpaulo IEnumWbemClassObject *pEnumerator; 491209139Srpaulo IWbemClassObject *pObj; 492209139Srpaulo ULONG uReturned; 493209139Srpaulo VARIANT vt; 494209139Srpaulo int len, pos; 495209139Srpaulo 496209139Srpaulo /* 497209139Srpaulo * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter 498209139Srpaulo * to have better probability of matching with InstanceName from 499209139Srpaulo * MSNdis events. If this fails, use the provided description. 500209139Srpaulo */ 501209139Srpaulo 502209139Srpaulo os_free(events->adapter_desc); 503209139Srpaulo events->adapter_desc = NULL; 504209139Srpaulo 505209139Srpaulo hr = call_IWbemLocator_ConnectServer( 506209139Srpaulo events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc); 507209139Srpaulo if (FAILED(hr)) { 508209139Srpaulo wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI " 509209139Srpaulo "server (ROOT\\CIMV2) - error 0x%x", (int) hr); 510209139Srpaulo return ndis_events_use_desc(events, desc); 511209139Srpaulo } 512209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2."); 513209139Srpaulo 514209139Srpaulo _snwprintf(query, MAX_QUERY_LEN, 515209139Srpaulo L"SELECT Index FROM Win32_NetworkAdapterConfiguration " 516209139Srpaulo L"WHERE SettingID='%S'", ifname); 517209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 518209139Srpaulo 519209139Srpaulo hr = call_IWbemServices_ExecQuery( 520209139Srpaulo pSvc, L"WQL", query, 521209139Srpaulo WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 522209139Srpaulo NULL, &pEnumerator); 523209139Srpaulo if (!SUCCEEDED(hr)) { 524209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " 525209139Srpaulo "GUID from Win32_NetworkAdapterConfiguration: " 526209139Srpaulo "0x%x", (int) hr); 527209139Srpaulo IWbemServices_Release(pSvc); 528209139Srpaulo return ndis_events_use_desc(events, desc); 529209139Srpaulo } 530209139Srpaulo 531209139Srpaulo uReturned = 0; 532209139Srpaulo hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, 533209139Srpaulo &pObj, &uReturned); 534209139Srpaulo if (!SUCCEEDED(hr) || uReturned == 0) { 535209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " 536209139Srpaulo "GUID from Win32_NetworkAdapterConfiguration: " 537209139Srpaulo "0x%x", (int) hr); 538209139Srpaulo IEnumWbemClassObject_Release(pEnumerator); 539209139Srpaulo IWbemServices_Release(pSvc); 540209139Srpaulo return ndis_events_use_desc(events, desc); 541209139Srpaulo } 542209139Srpaulo IEnumWbemClassObject_Release(pEnumerator); 543209139Srpaulo 544209139Srpaulo VariantInit(&vt); 545209139Srpaulo hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL); 546209139Srpaulo if (!SUCCEEDED(hr)) { 547209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from " 548209139Srpaulo "Win32_NetworkAdapterConfiguration: 0x%x", 549209139Srpaulo (int) hr); 550209139Srpaulo IWbemServices_Release(pSvc); 551209139Srpaulo return ndis_events_use_desc(events, desc); 552209139Srpaulo } 553209139Srpaulo 554209139Srpaulo _snwprintf(query, MAX_QUERY_LEN, 555209139Srpaulo L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE " 556209139Srpaulo L"Index=%d", 557209139Srpaulo vt.uintVal); 558209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 559209139Srpaulo VariantClear(&vt); 560209139Srpaulo IWbemClassObject_Release(pObj); 561209139Srpaulo 562209139Srpaulo hr = call_IWbemServices_ExecQuery( 563209139Srpaulo pSvc, L"WQL", query, 564209139Srpaulo WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 565209139Srpaulo NULL, &pEnumerator); 566209139Srpaulo if (!SUCCEEDED(hr)) { 567209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " 568209139Srpaulo "from Win32_NetworkAdapter: 0x%x", (int) hr); 569209139Srpaulo IWbemServices_Release(pSvc); 570209139Srpaulo return ndis_events_use_desc(events, desc); 571209139Srpaulo } 572209139Srpaulo 573209139Srpaulo uReturned = 0; 574209139Srpaulo hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, 575209139Srpaulo &pObj, &uReturned); 576209139Srpaulo if (!SUCCEEDED(hr) || uReturned == 0) { 577209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " 578209139Srpaulo "from Win32_NetworkAdapter: 0x%x", (int) hr); 579209139Srpaulo IEnumWbemClassObject_Release(pEnumerator); 580209139Srpaulo IWbemServices_Release(pSvc); 581209139Srpaulo return ndis_events_use_desc(events, desc); 582209139Srpaulo } 583209139Srpaulo IEnumWbemClassObject_Release(pEnumerator); 584209139Srpaulo 585209139Srpaulo hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); 586209139Srpaulo if (!SUCCEEDED(hr)) { 587209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " 588209139Srpaulo "Win32_NetworkAdapter: 0x%x", (int) hr); 589209139Srpaulo IWbemClassObject_Release(pObj); 590209139Srpaulo IWbemServices_Release(pSvc); 591209139Srpaulo return ndis_events_use_desc(events, desc); 592209139Srpaulo } 593209139Srpaulo 594209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'", 595209139Srpaulo vt.bstrVal); 596209139Srpaulo events->adapter_desc = _wcsdup(vt.bstrVal); 597209139Srpaulo VariantClear(&vt); 598209139Srpaulo 599209139Srpaulo /* 600209139Srpaulo * Try to get even better candidate for matching with InstanceName 601209139Srpaulo * from Win32_PnPEntity. This is needed at least for some USB cards 602209139Srpaulo * that can change the InstanceName whenever being unplugged and 603209139Srpaulo * plugged again. 604209139Srpaulo */ 605209139Srpaulo 606209139Srpaulo hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL); 607209139Srpaulo if (!SUCCEEDED(hr)) { 608209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID " 609209139Srpaulo "from Win32_NetworkAdapter: 0x%x", (int) hr); 610209139Srpaulo IWbemClassObject_Release(pObj); 611209139Srpaulo IWbemServices_Release(pSvc); 612209139Srpaulo if (events->adapter_desc == NULL) 613209139Srpaulo return ndis_events_use_desc(events, desc); 614209139Srpaulo return 0; /* use Win32_NetworkAdapter::Name */ 615209139Srpaulo } 616209139Srpaulo 617209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID=" 618209139Srpaulo "'%S'", vt.bstrVal); 619209139Srpaulo 620209139Srpaulo len = _snwprintf(query, MAX_QUERY_LEN, 621209139Srpaulo L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='"); 622209139Srpaulo if (len < 0 || len >= MAX_QUERY_LEN - 1) { 623209139Srpaulo VariantClear(&vt); 624209139Srpaulo IWbemClassObject_Release(pObj); 625209139Srpaulo IWbemServices_Release(pSvc); 626209139Srpaulo if (events->adapter_desc == NULL) 627209139Srpaulo return ndis_events_use_desc(events, desc); 628209139Srpaulo return 0; /* use Win32_NetworkAdapter::Name */ 629209139Srpaulo } 630209139Srpaulo 631209139Srpaulo /* Escape \ as \\ */ 632209139Srpaulo for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) { 633209139Srpaulo if (vt.bstrVal[pos] == '\\') { 634209139Srpaulo if (len >= MAX_QUERY_LEN - 3) 635209139Srpaulo break; 636209139Srpaulo query[len++] = '\\'; 637209139Srpaulo } 638209139Srpaulo query[len++] = vt.bstrVal[pos]; 639209139Srpaulo } 640209139Srpaulo query[len++] = L'\''; 641209139Srpaulo query[len] = L'\0'; 642209139Srpaulo VariantClear(&vt); 643209139Srpaulo IWbemClassObject_Release(pObj); 644209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); 645209139Srpaulo 646209139Srpaulo hr = call_IWbemServices_ExecQuery( 647209139Srpaulo pSvc, L"WQL", query, 648209139Srpaulo WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 649209139Srpaulo NULL, &pEnumerator); 650209139Srpaulo if (!SUCCEEDED(hr)) { 651209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " 652209139Srpaulo "Name from Win32_PnPEntity: 0x%x", (int) hr); 653209139Srpaulo IWbemServices_Release(pSvc); 654209139Srpaulo if (events->adapter_desc == NULL) 655209139Srpaulo return ndis_events_use_desc(events, desc); 656209139Srpaulo return 0; /* use Win32_NetworkAdapter::Name */ 657209139Srpaulo } 658209139Srpaulo 659209139Srpaulo uReturned = 0; 660209139Srpaulo hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, 661209139Srpaulo &pObj, &uReturned); 662209139Srpaulo if (!SUCCEEDED(hr) || uReturned == 0) { 663209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " 664209139Srpaulo "from Win32_PnPEntity: 0x%x", (int) hr); 665209139Srpaulo IEnumWbemClassObject_Release(pEnumerator); 666209139Srpaulo IWbemServices_Release(pSvc); 667209139Srpaulo if (events->adapter_desc == NULL) 668209139Srpaulo return ndis_events_use_desc(events, desc); 669209139Srpaulo return 0; /* use Win32_NetworkAdapter::Name */ 670209139Srpaulo } 671209139Srpaulo IEnumWbemClassObject_Release(pEnumerator); 672209139Srpaulo 673209139Srpaulo hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); 674209139Srpaulo if (!SUCCEEDED(hr)) { 675209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " 676209139Srpaulo "Win32_PnPEntity: 0x%x", (int) hr); 677209139Srpaulo IWbemClassObject_Release(pObj); 678209139Srpaulo IWbemServices_Release(pSvc); 679209139Srpaulo if (events->adapter_desc == NULL) 680209139Srpaulo return ndis_events_use_desc(events, desc); 681209139Srpaulo return 0; /* use Win32_NetworkAdapter::Name */ 682209139Srpaulo } 683209139Srpaulo 684209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'", 685209139Srpaulo vt.bstrVal); 686209139Srpaulo os_free(events->adapter_desc); 687209139Srpaulo events->adapter_desc = _wcsdup(vt.bstrVal); 688209139Srpaulo VariantClear(&vt); 689209139Srpaulo 690209139Srpaulo IWbemClassObject_Release(pObj); 691209139Srpaulo 692209139Srpaulo IWbemServices_Release(pSvc); 693209139Srpaulo 694209139Srpaulo if (events->adapter_desc == NULL) 695209139Srpaulo return ndis_events_use_desc(events, desc); 696209139Srpaulo 697209139Srpaulo return 0; 698209139Srpaulo} 699209139Srpaulo 700209139Srpaulo 701209139Srpaulostruct ndis_events_data * 702209139Srpaulondis_events_init(HANDLE *read_pipe, HANDLE *event_avail, 703209139Srpaulo const char *ifname, const char *desc) 704209139Srpaulo{ 705209139Srpaulo HRESULT hr; 706209139Srpaulo IWbemObjectSink *pSink; 707209139Srpaulo struct ndis_events_data *events; 708209139Srpaulo 709209139Srpaulo events = os_zalloc(sizeof(*events)); 710209139Srpaulo if (events == NULL) { 711209139Srpaulo wpa_printf(MSG_ERROR, "Could not allocate sink for events."); 712209139Srpaulo return NULL; 713209139Srpaulo } 714209139Srpaulo events->ifname = os_strdup(ifname); 715209139Srpaulo if (events->ifname == NULL) { 716209139Srpaulo os_free(events); 717209139Srpaulo return NULL; 718209139Srpaulo } 719209139Srpaulo 720209139Srpaulo if (wmi_refcnt++ == 0) { 721209139Srpaulo hr = CoInitializeEx(0, COINIT_MULTITHREADED); 722209139Srpaulo if (FAILED(hr)) { 723209139Srpaulo wpa_printf(MSG_ERROR, "CoInitializeEx() failed - " 724209139Srpaulo "returned 0x%x", (int) hr); 725209139Srpaulo os_free(events); 726209139Srpaulo return NULL; 727209139Srpaulo } 728209139Srpaulo } 729209139Srpaulo 730209139Srpaulo if (wmi_first) { 731209139Srpaulo /* CoInitializeSecurity() must be called once and only once 732209139Srpaulo * per process, so let's use wmi_first flag to protect against 733209139Srpaulo * multiple calls. */ 734209139Srpaulo wmi_first = 0; 735209139Srpaulo 736209139Srpaulo hr = CoInitializeSecurity(NULL, -1, NULL, NULL, 737209139Srpaulo RPC_C_AUTHN_LEVEL_PKT_PRIVACY, 738209139Srpaulo RPC_C_IMP_LEVEL_IMPERSONATE, 739209139Srpaulo NULL, EOAC_SECURE_REFS, NULL); 740209139Srpaulo if (FAILED(hr)) { 741209139Srpaulo wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed " 742209139Srpaulo "- returned 0x%x", (int) hr); 743209139Srpaulo os_free(events); 744209139Srpaulo return NULL; 745209139Srpaulo } 746209139Srpaulo } 747209139Srpaulo 748209139Srpaulo hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, 749209139Srpaulo &IID_IWbemLocator, 750209139Srpaulo (LPVOID *) (void *) &events->pLoc); 751209139Srpaulo if (FAILED(hr)) { 752209139Srpaulo wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " 753209139Srpaulo "0x%x", (int) hr); 754209139Srpaulo CoUninitialize(); 755209139Srpaulo os_free(events); 756209139Srpaulo return NULL; 757209139Srpaulo } 758209139Srpaulo 759209139Srpaulo if (ndis_events_get_adapter(events, ifname, desc) < 0) { 760209139Srpaulo CoUninitialize(); 761209139Srpaulo os_free(events); 762209139Srpaulo return NULL; 763209139Srpaulo } 764209139Srpaulo wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", 765209139Srpaulo events->adapter_desc); 766209139Srpaulo 767209139Srpaulo hr = call_IWbemLocator_ConnectServer( 768209139Srpaulo events->pLoc, L"ROOT\\WMI", NULL, NULL, 769209139Srpaulo 0, 0, 0, 0, &events->pSvc); 770209139Srpaulo if (FAILED(hr)) { 771209139Srpaulo wpa_printf(MSG_ERROR, "Could not connect to server - error " 772209139Srpaulo "0x%x", (int) hr); 773209139Srpaulo CoUninitialize(); 774209139Srpaulo os_free(events->adapter_desc); 775209139Srpaulo os_free(events); 776209139Srpaulo return NULL; 777209139Srpaulo } 778209139Srpaulo wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); 779209139Srpaulo 780209139Srpaulo ndis_events_constructor(events); 781209139Srpaulo pSink = &events->sink; 782209139Srpaulo pSink->lpVtbl = &events->sink_vtbl; 783209139Srpaulo events->sink_vtbl.QueryInterface = ndis_events_query_interface; 784209139Srpaulo events->sink_vtbl.AddRef = ndis_events_add_ref; 785209139Srpaulo events->sink_vtbl.Release = ndis_events_release; 786209139Srpaulo events->sink_vtbl.Indicate = ndis_events_indicate; 787209139Srpaulo events->sink_vtbl.SetStatus = ndis_events_set_status; 788209139Srpaulo 789209139Srpaulo if (register_async_notification(pSink, events->pSvc) < 0) { 790209139Srpaulo wpa_printf(MSG_DEBUG, "Failed to register async " 791209139Srpaulo "notifications"); 792209139Srpaulo ndis_events_destructor(events); 793209139Srpaulo os_free(events->adapter_desc); 794209139Srpaulo os_free(events); 795209139Srpaulo return NULL; 796209139Srpaulo } 797209139Srpaulo 798209139Srpaulo *read_pipe = events->read_pipe; 799209139Srpaulo *event_avail = events->event_avail; 800209139Srpaulo 801209139Srpaulo return events; 802209139Srpaulo} 803