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