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