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 */
call_IWbemServices_ExecQuery(IWbemServices * pSvc,LPCWSTR strQueryLanguage,LPCWSTR strQuery,long lFlags,IWbemContext * pCtx,IEnumWbemClassObject ** ppEnum)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
call_IWbemServices_ExecNotificationQueryAsync(IWbemServices * pSvc,LPCWSTR strQueryLanguage,LPCWSTR strQuery,long lFlags,IWbemContext * pCtx,IWbemObjectSink * pResponseHandler)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
call_IWbemLocator_ConnectServer(IWbemLocator * pLoc,LPCWSTR strNetworkResource,LPCWSTR strUser,LPCWSTR strPassword,LPCWSTR strLocale,long lSecurityFlags,LPCWSTR strAuthority,IWbemContext * pCtx,IWbemServices ** ppNamespace)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
ndis_events_constructor(struct ndis_events_data * events)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
ndis_events_destructor(struct ndis_events_data * events)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
ndis_events_query_interface(IWbemObjectSink * this,REFIID riid,void ** obj)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
ndis_events_add_ref(IWbemObjectSink * this)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
ndis_events_release(IWbemObjectSink * this)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
ndis_events_send_event(struct ndis_events_data * events,enum event_types type,char * data,size_t data_len)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
ndis_events_media_connect(struct ndis_events_data * events)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
ndis_events_media_disconnect(struct ndis_events_data * events)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
ndis_events_media_specific(struct ndis_events_data * events,IWbemClassObject * pObj)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
ndis_events_adapter_arrival(struct ndis_events_data * events)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
ndis_events_adapter_removal(struct ndis_events_data * events)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
ndis_events_indicate(IWbemObjectSink * this,long lObjectCount,IWbemClassObject __RPC_FAR * __RPC_FAR * ppObjArray)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
ndis_events_set_status(IWbemObjectSink * this,long lFlags,HRESULT hResult,BSTR strParam,IWbemClassObject __RPC_FAR * pObjParam)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
notification_query(IWbemObjectSink * pDestSink,IWbemServices * pSvc,const char * class_name)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
register_async_notification(IWbemObjectSink * pDestSink,IWbemServices * pSvc)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
ndis_events_deinit(struct ndis_events_data * events)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
ndis_events_use_desc(struct ndis_events_data * events,const char * desc)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
ndis_events_get_adapter(struct ndis_events_data * events,const char * ifname,const char * desc)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 *
ndis_events_init(HANDLE * read_pipe,HANDLE * event_avail,const char * ifname,const char * desc)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