Merge branch 'vendor/TNFTP'
[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 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 */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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, "Unepected event - __CLASS: "
376                                    "'%S'", vtClass.bstrVal);
377                 }
378
379                 VariantClear(&vtClass);
380         }
381
382         return WBEM_NO_ERROR;
383 }
384
385
386 static HRESULT STDMETHODCALLTYPE
387 ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
388                        BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
389 {
390         return WBEM_NO_ERROR;
391 }
392
393
394 static int notification_query(IWbemObjectSink *pDestSink,
395                               IWbemServices *pSvc, const char *class_name)
396 {
397         HRESULT hr;
398         WCHAR query[256];
399
400         _snwprintf(query, 256,
401                   L"SELECT * FROM %S", class_name);
402         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
403         hr = call_IWbemServices_ExecNotificationQueryAsync(
404                 pSvc, L"WQL", query, 0, 0, pDestSink);
405         if (FAILED(hr)) {
406                 wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
407                            "failed with hresult of 0x%x",
408                            class_name, (int) hr);
409                 return -1;
410         }
411
412         return 0;
413 }
414
415
416 static int register_async_notification(IWbemObjectSink *pDestSink,
417                                        IWbemServices *pSvc)
418 {
419         int i;
420         const char *class_list[] = {
421                 "MSNdis_StatusMediaConnect",
422                 "MSNdis_StatusMediaDisconnect",
423                 "MSNdis_StatusMediaSpecificIndication",
424                 "MSNdis_NotifyAdapterArrival",
425                 "MSNdis_NotifyAdapterRemoval",
426                 NULL
427         };
428
429         for (i = 0; class_list[i]; i++) {
430                 if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
431                         return -1;
432         }
433
434         return 0;
435 }
436
437
438 void ndis_events_deinit(struct ndis_events_data *events)
439 {
440         events->terminating = 1;
441         IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
442         IWbemObjectSink_Release(&events->sink);
443         /*
444          * Rest of deinitialization is done in ndis_events_destructor() once
445          * all reference count drops to zero.
446          */
447 }
448
449
450 static int ndis_events_use_desc(struct ndis_events_data *events,
451                                 const char *desc)
452 {
453         char *tmp, *pos;
454         size_t len;
455
456         if (desc == NULL) {
457                 if (events->adapter_desc == NULL)
458                         return -1;
459                 /* Continue using old description */
460                 return 0;
461         }
462
463         tmp = os_strdup(desc);
464         if (tmp == NULL)
465                 return -1;
466
467         pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
468         if (pos)
469                 *pos = '\0';
470
471         len = os_strlen(tmp);
472         events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
473         if (events->adapter_desc == NULL) {
474                 os_free(tmp);
475                 return -1;
476         }
477         _snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
478         os_free(tmp);
479         return 0;
480 }
481
482
483 static int ndis_events_get_adapter(struct ndis_events_data *events,
484                                    const char *ifname, const char *desc)
485 {
486         HRESULT hr;
487         IWbemServices *pSvc;
488 #define MAX_QUERY_LEN 256
489         WCHAR query[MAX_QUERY_LEN];
490         IEnumWbemClassObject *pEnumerator;
491         IWbemClassObject *pObj;
492         ULONG uReturned;
493         VARIANT vt;
494         int len, pos;
495
496         /*
497          * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
498          * to have better probability of matching with InstanceName from
499          * MSNdis events. If this fails, use the provided description.
500          */
501
502         os_free(events->adapter_desc);
503         events->adapter_desc = NULL;
504
505         hr = call_IWbemLocator_ConnectServer(
506                 events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
507         if (FAILED(hr)) {
508                 wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
509                            "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
510                 return ndis_events_use_desc(events, desc);
511         }
512         wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
513
514         _snwprintf(query, MAX_QUERY_LEN,
515                   L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
516                   L"WHERE SettingID='%S'", ifname);
517         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
518
519         hr = call_IWbemServices_ExecQuery(
520                 pSvc, L"WQL", query,
521                 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
522                 NULL, &pEnumerator);
523         if (!SUCCEEDED(hr)) {
524                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
525                            "GUID from Win32_NetworkAdapterConfiguration: "
526                            "0x%x", (int) hr);
527                 IWbemServices_Release(pSvc);
528                 return ndis_events_use_desc(events, desc);
529         }
530
531         uReturned = 0;
532         hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
533                                        &pObj, &uReturned);
534         if (!SUCCEEDED(hr) || uReturned == 0) {
535                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
536                            "GUID from Win32_NetworkAdapterConfiguration: "
537                            "0x%x", (int) hr);
538                 IEnumWbemClassObject_Release(pEnumerator);
539                 IWbemServices_Release(pSvc);
540                 return ndis_events_use_desc(events, desc);
541         }
542         IEnumWbemClassObject_Release(pEnumerator);
543
544         VariantInit(&vt);
545         hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
546         if (!SUCCEEDED(hr)) {
547                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
548                            "Win32_NetworkAdapterConfiguration: 0x%x",
549                            (int) hr);
550                 IWbemServices_Release(pSvc);
551                 return ndis_events_use_desc(events, desc);
552         }
553
554         _snwprintf(query, MAX_QUERY_LEN,
555                   L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
556                   L"Index=%d",
557                   vt.uintVal);
558         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
559         VariantClear(&vt);
560         IWbemClassObject_Release(pObj);
561
562         hr = call_IWbemServices_ExecQuery(
563                 pSvc, L"WQL", query,
564                 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
565                 NULL, &pEnumerator);
566         if (!SUCCEEDED(hr)) {
567                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
568                            "from Win32_NetworkAdapter: 0x%x", (int) hr);
569                 IWbemServices_Release(pSvc);
570                 return ndis_events_use_desc(events, desc);
571         }
572
573         uReturned = 0;
574         hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
575                                        &pObj, &uReturned);
576         if (!SUCCEEDED(hr) || uReturned == 0) {
577                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
578                            "from Win32_NetworkAdapter: 0x%x", (int) hr);
579                 IEnumWbemClassObject_Release(pEnumerator);
580                 IWbemServices_Release(pSvc);
581                 return ndis_events_use_desc(events, desc);
582         }
583         IEnumWbemClassObject_Release(pEnumerator);
584
585         hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
586         if (!SUCCEEDED(hr)) {
587                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
588                            "Win32_NetworkAdapter: 0x%x", (int) hr);
589                 IWbemClassObject_Release(pObj);
590                 IWbemServices_Release(pSvc);
591                 return ndis_events_use_desc(events, desc);
592         }
593
594         wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
595                    vt.bstrVal);
596         events->adapter_desc = _wcsdup(vt.bstrVal);
597         VariantClear(&vt);
598
599         /*
600          * Try to get even better candidate for matching with InstanceName
601          * from Win32_PnPEntity. This is needed at least for some USB cards
602          * that can change the InstanceName whenever being unplugged and
603          * plugged again.
604          */
605
606         hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
607         if (!SUCCEEDED(hr)) {
608                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
609                            "from Win32_NetworkAdapter: 0x%x", (int) hr);
610                 IWbemClassObject_Release(pObj);
611                 IWbemServices_Release(pSvc);
612                 if (events->adapter_desc == NULL)
613                         return ndis_events_use_desc(events, desc);
614                 return 0; /* use Win32_NetworkAdapter::Name */
615         }
616
617         wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
618                    "'%S'", vt.bstrVal);
619
620         len = _snwprintf(query, MAX_QUERY_LEN,
621                         L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
622         if (len < 0 || len >= MAX_QUERY_LEN - 1) {
623                 VariantClear(&vt);
624                 IWbemClassObject_Release(pObj);
625                 IWbemServices_Release(pSvc);
626                 if (events->adapter_desc == NULL)
627                         return ndis_events_use_desc(events, desc);
628                 return 0; /* use Win32_NetworkAdapter::Name */
629         }
630
631         /* Escape \ as \\ */
632         for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
633                 if (vt.bstrVal[pos] == '\\') {
634                         if (len >= MAX_QUERY_LEN - 3)
635                                 break;
636                         query[len++] = '\\';
637                 }
638                 query[len++] = vt.bstrVal[pos];
639         }
640         query[len++] = L'\'';
641         query[len] = L'\0';
642         VariantClear(&vt);
643         IWbemClassObject_Release(pObj);
644         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
645
646         hr = call_IWbemServices_ExecQuery(
647                 pSvc, L"WQL", query,
648                 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
649                 NULL, &pEnumerator);
650         if (!SUCCEEDED(hr)) {
651                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
652                            "Name from Win32_PnPEntity: 0x%x", (int) hr);
653                 IWbemServices_Release(pSvc);
654                 if (events->adapter_desc == NULL)
655                         return ndis_events_use_desc(events, desc);
656                 return 0; /* use Win32_NetworkAdapter::Name */
657         }
658
659         uReturned = 0;
660         hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
661                                        &pObj, &uReturned);
662         if (!SUCCEEDED(hr) || uReturned == 0) {
663                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
664                            "from Win32_PnPEntity: 0x%x", (int) hr);
665                 IEnumWbemClassObject_Release(pEnumerator);
666                 IWbemServices_Release(pSvc);
667                 if (events->adapter_desc == NULL)
668                         return ndis_events_use_desc(events, desc);
669                 return 0; /* use Win32_NetworkAdapter::Name */
670         }
671         IEnumWbemClassObject_Release(pEnumerator);
672
673         hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
674         if (!SUCCEEDED(hr)) {
675                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
676                            "Win32_PnPEntity: 0x%x", (int) hr);
677                 IWbemClassObject_Release(pObj);
678                 IWbemServices_Release(pSvc);
679                 if (events->adapter_desc == NULL)
680                         return ndis_events_use_desc(events, desc);
681                 return 0; /* use Win32_NetworkAdapter::Name */
682         }
683
684         wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
685                    vt.bstrVal);
686         os_free(events->adapter_desc);
687         events->adapter_desc = _wcsdup(vt.bstrVal);
688         VariantClear(&vt);
689
690         IWbemClassObject_Release(pObj);
691
692         IWbemServices_Release(pSvc);
693
694         if (events->adapter_desc == NULL)
695                 return ndis_events_use_desc(events, desc);
696
697         return 0;
698 }
699
700
701 struct ndis_events_data *
702 ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
703                  const char *ifname, const char *desc)
704 {
705         HRESULT hr;
706         IWbemObjectSink *pSink;
707         struct ndis_events_data *events;
708
709         events = os_zalloc(sizeof(*events));
710         if (events == NULL) {
711                 wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
712                 return NULL;
713         }
714         events->ifname = os_strdup(ifname);
715         if (events->ifname == NULL) {
716                 os_free(events);
717                 return NULL;
718         }
719
720         if (wmi_refcnt++ == 0) {
721                 hr = CoInitializeEx(0, COINIT_MULTITHREADED);
722                 if (FAILED(hr)) {
723                         wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
724                                    "returned 0x%x", (int) hr);
725                         os_free(events);
726                         return NULL;
727                 }
728         }
729
730         if (wmi_first) {
731                 /* CoInitializeSecurity() must be called once and only once
732                  * per process, so let's use wmi_first flag to protect against
733                  * multiple calls. */
734                 wmi_first = 0;
735
736                 hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
737                                           RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
738                                           RPC_C_IMP_LEVEL_IMPERSONATE,
739                                           NULL, EOAC_SECURE_REFS, NULL);
740                 if (FAILED(hr)) {
741                         wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
742                                    "- returned 0x%x", (int) hr);
743                         os_free(events);
744                         return NULL;
745                 }
746         }
747
748         hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
749                               &IID_IWbemLocator,
750                               (LPVOID *) (void *) &events->pLoc);
751         if (FAILED(hr)) {
752                 wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
753                            "0x%x", (int) hr);
754                 CoUninitialize();
755                 os_free(events);
756                 return NULL;
757         }
758
759         if (ndis_events_get_adapter(events, ifname, desc) < 0) {
760                 CoUninitialize();
761                 os_free(events);
762                 return NULL;
763         }
764         wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
765                    events->adapter_desc);
766
767         hr = call_IWbemLocator_ConnectServer(
768                 events->pLoc, L"ROOT\\WMI", NULL, NULL,
769                 0, 0, 0, 0, &events->pSvc);
770         if (FAILED(hr)) {
771                 wpa_printf(MSG_ERROR, "Could not connect to server - error "
772                            "0x%x", (int) hr);
773                 CoUninitialize();
774                 os_free(events->adapter_desc);
775                 os_free(events);
776                 return NULL;
777         }
778         wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
779
780         ndis_events_constructor(events);
781         pSink = &events->sink;
782         pSink->lpVtbl = &events->sink_vtbl;
783         events->sink_vtbl.QueryInterface = ndis_events_query_interface;
784         events->sink_vtbl.AddRef = ndis_events_add_ref;
785         events->sink_vtbl.Release = ndis_events_release;
786         events->sink_vtbl.Indicate = ndis_events_indicate;
787         events->sink_vtbl.SetStatus = ndis_events_set_status;
788
789         if (register_async_notification(pSink, events->pSvc) < 0) {
790                 wpa_printf(MSG_DEBUG, "Failed to register async "
791                            "notifications");
792                 ndis_events_destructor(events);
793                 os_free(events->adapter_desc);
794                 os_free(events);
795                 return NULL;
796         }
797
798         *read_pipe = events->read_pipe;
799         *event_avail = events->event_avail;
800
801         return events;
802 }