2
* ndis_events - Receive NdisMIndicateStatus() events using WMI
3
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
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.
9
* Alternatively, this software may be distributed under the terms of BSD
12
* See README and COPYING for more details.
15
#define _WIN32_WINNT 0x0400
21
#endif /* COBJMACROS */
27
static int wmi_refcnt = 0;
28
static int wmi_first = 1;
30
struct ndis_events_data {
32
IWbemObjectSinkVtbl sink_vtbl;
37
HANDLE read_pipe, write_pipe, event_avail;
40
char *ifname; /* {GUID..} */
44
enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
45
EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
47
static int ndis_events_get_adapter(struct ndis_events_data *events,
48
const char *ifname, const char *desc);
51
static int ndis_events_constructor(struct ndis_events_data *events)
55
if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
56
wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
57
(int) GetLastError());
60
events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
61
if (events->event_avail == NULL) {
62
wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
63
(int) GetLastError());
64
CloseHandle(events->read_pipe);
65
CloseHandle(events->write_pipe);
73
static void ndis_events_destructor(struct ndis_events_data *events)
75
CloseHandle(events->read_pipe);
76
CloseHandle(events->write_pipe);
77
CloseHandle(events->event_avail);
78
IWbemServices_Release(events->pSvc);
79
IWbemLocator_Release(events->pLoc);
80
if (--wmi_refcnt == 0)
85
static HRESULT STDMETHODCALLTYPE
86
ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
90
if (IsEqualIID(riid, &IID_IUnknown) ||
91
IsEqualIID(riid, &IID_IWbemObjectSink)) {
93
IWbemObjectSink_AddRef(this);
101
static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
103
struct ndis_events_data *events = (struct ndis_events_data *) this;
104
return ++events->ref;
108
static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
110
struct ndis_events_data *events = (struct ndis_events_data *) this;
112
if (--events->ref != 0)
115
ndis_events_destructor(events);
116
wpa_printf(MSG_DEBUG, "ndis_events: terminated");
117
os_free(events->adapter_desc);
118
os_free(events->ifname);
124
static int ndis_events_send_event(struct ndis_events_data *events,
125
enum event_types type,
126
char *data, size_t data_len)
128
char buf[512], *pos, *end;
132
end = buf + sizeof(buf);
134
os_memcpy(buf, &_type, sizeof(_type));
135
pos = buf + sizeof(_type);
138
if (2 + data_len > (size_t) (end - pos)) {
139
wpa_printf(MSG_DEBUG, "Not enough room for send_event "
140
"data (%d)", data_len);
143
*pos++ = data_len >> 8;
144
*pos++ = data_len & 0xff;
145
os_memcpy(pos, data, data_len);
149
if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
150
SetEvent(events->event_avail);
153
wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
158
static void ndis_events_media_connect(struct ndis_events_data *events)
160
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
161
ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
165
static void ndis_events_media_disconnect(struct ndis_events_data *events)
167
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
168
ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
172
static void ndis_events_media_specific(struct ndis_events_data *events,
173
IWbemClassObject *pObj)
177
LONG lower, upper, k;
182
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
184
/* This is the StatusBuffer from NdisMIndicateStatus() call */
185
hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
188
wpa_printf(MSG_DEBUG, "Could not get "
189
"NdisStatusMediaSpecificIndication from "
194
SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
195
SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
196
data_len = upper - lower + 1;
197
data = os_malloc(data_len);
199
wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
206
for (k = lower; k <= upper; k++) {
207
SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
210
wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", data, data_len);
214
ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
220
static void ndis_events_adapter_arrival(struct ndis_events_data *events)
222
wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
223
ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
227
static void ndis_events_adapter_removal(struct ndis_events_data *events)
229
wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
230
ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
234
static HRESULT STDMETHODCALLTYPE
235
ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
236
IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
238
struct ndis_events_data *events = (struct ndis_events_data *) this;
241
if (events->terminating) {
242
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
243
"indication - terminating");
244
return WBEM_NO_ERROR;
246
/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
249
for (i = 0; i < lObjectCount; i++) {
250
IWbemClassObject *pObj = ppObjArray[i];
254
hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
257
wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
261
/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
263
hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
266
wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
268
VariantClear(&vtClass);
272
if (wcscmp(vtClass.bstrVal,
273
L"MSNdis_NotifyAdapterArrival") == 0) {
274
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
275
"update adapter description since it may "
276
"have changed with new adapter instance");
277
ndis_events_get_adapter(events, events->ifname, NULL);
280
if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
281
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
282
"indication for foreign adapter: "
283
"InstanceName: '%S' __CLASS: '%S'",
284
vt.bstrVal, vtClass.bstrVal);
285
VariantClear(&vtClass);
291
if (wcscmp(vtClass.bstrVal,
292
L"MSNdis_StatusMediaSpecificIndication") == 0) {
293
ndis_events_media_specific(events, pObj);
294
} else if (wcscmp(vtClass.bstrVal,
295
L"MSNdis_StatusMediaConnect") == 0) {
296
ndis_events_media_connect(events);
297
} else if (wcscmp(vtClass.bstrVal,
298
L"MSNdis_StatusMediaDisconnect") == 0) {
299
ndis_events_media_disconnect(events);
300
} else if (wcscmp(vtClass.bstrVal,
301
L"MSNdis_NotifyAdapterArrival") == 0) {
302
ndis_events_adapter_arrival(events);
303
} else if (wcscmp(vtClass.bstrVal,
304
L"MSNdis_NotifyAdapterRemoval") == 0) {
305
ndis_events_adapter_removal(events);
307
wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
308
"'%S'", vtClass.bstrVal);
311
VariantClear(&vtClass);
314
return WBEM_NO_ERROR;
318
static HRESULT STDMETHODCALLTYPE
319
ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
320
BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
322
return WBEM_NO_ERROR;
326
static int notification_query(IWbemObjectSink *pDestSink,
327
IWbemServices *pSvc, const char *class_name)
332
_snwprintf(query, 256,
333
L"SELECT * FROM %S", class_name);
334
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
335
hr = IWbemServices_ExecNotificationQueryAsync(pSvc, L"WQL", query, 0,
338
wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
339
"failed with hresult of 0x%x",
340
class_name, (int) hr);
348
static int register_async_notification(IWbemObjectSink *pDestSink,
352
const char *class_list[] = {
353
"MSNdis_StatusMediaConnect",
354
"MSNdis_StatusMediaDisconnect",
355
"MSNdis_StatusMediaSpecificIndication",
356
"MSNdis_NotifyAdapterArrival",
357
"MSNdis_NotifyAdapterRemoval",
361
for (i = 0; class_list[i]; i++) {
362
if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
370
void ndis_events_deinit(struct ndis_events_data *events)
372
events->terminating = 1;
373
IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
374
IWbemObjectSink_Release(&events->sink);
376
* Rest of deinitialization is done in ndis_events_destructor() once
377
* all reference count drops to zero.
382
static int ndis_events_use_desc(struct ndis_events_data *events,
389
if (events->adapter_desc == NULL)
391
/* Continue using old description */
395
tmp = os_strdup(desc);
399
pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
403
len = os_strlen(tmp);
404
events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
405
if (events->adapter_desc == NULL) {
409
_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
415
static int ndis_events_get_adapter(struct ndis_events_data *events,
416
const char *ifname, const char *desc)
420
#define MAX_QUERY_LEN 256
421
WCHAR query[MAX_QUERY_LEN];
422
IEnumWbemClassObject *pEnumerator;
423
IWbemClassObject *pObj;
429
* Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
430
* to have better probability of matching with InstanceName from
431
* MSNdis events. If this fails, use the provided description.
434
os_free(events->adapter_desc);
435
events->adapter_desc = NULL;
437
hr = IWbemLocator_ConnectServer(events->pLoc, L"ROOT\\CIMV2", NULL,
438
NULL, 0, 0, 0, 0, &pSvc);
440
wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
441
"server (ROOT\\CIMV2) - error 0x%x", (int) hr);
442
return ndis_events_use_desc(events, desc);
444
wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
446
_snwprintf(query, MAX_QUERY_LEN,
447
L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
448
L"WHERE SettingID='%S'", ifname);
449
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
451
hr = IWbemServices_ExecQuery(pSvc, L"WQL", query,
452
WBEM_FLAG_FORWARD_ONLY |
453
WBEM_FLAG_RETURN_IMMEDIATELY,
455
if (!SUCCEEDED(hr)) {
456
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
457
"GUID from Win32_NetworkAdapterConfiguration: "
459
IWbemServices_Release(pSvc);
460
return ndis_events_use_desc(events, desc);
464
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
466
if (!SUCCEEDED(hr) || uReturned == 0) {
467
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
468
"GUID from Win32_NetworkAdapterConfiguration: "
470
IEnumWbemClassObject_Release(pEnumerator);
471
IWbemServices_Release(pSvc);
472
return ndis_events_use_desc(events, desc);
474
IEnumWbemClassObject_Release(pEnumerator);
477
hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
478
if (!SUCCEEDED(hr)) {
479
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
480
"Win32_NetworkAdapterConfiguration: 0x%x",
482
IWbemServices_Release(pSvc);
483
return ndis_events_use_desc(events, desc);
486
_snwprintf(query, MAX_QUERY_LEN,
487
L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
490
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
492
IWbemClassObject_Release(pObj);
494
hr = IWbemServices_ExecQuery(pSvc, L"WQL", query,
495
WBEM_FLAG_FORWARD_ONLY |
496
WBEM_FLAG_RETURN_IMMEDIATELY,
498
if (!SUCCEEDED(hr)) {
499
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
500
"from Win32_NetworkAdapter: 0x%x", (int) hr);
501
IWbemServices_Release(pSvc);
502
return ndis_events_use_desc(events, desc);
506
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
508
if (!SUCCEEDED(hr) || uReturned == 0) {
509
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
510
"from Win32_NetworkAdapter: 0x%x", (int) hr);
511
IEnumWbemClassObject_Release(pEnumerator);
512
IWbemServices_Release(pSvc);
513
return ndis_events_use_desc(events, desc);
515
IEnumWbemClassObject_Release(pEnumerator);
517
hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
518
if (!SUCCEEDED(hr)) {
519
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
520
"Win32_NetworkAdapter: 0x%x", (int) hr);
521
IWbemClassObject_Release(pObj);
522
IWbemServices_Release(pSvc);
523
return ndis_events_use_desc(events, desc);
526
wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
528
events->adapter_desc = _wcsdup(vt.bstrVal);
532
* Try to get even better candidate for matching with InstanceName
533
* from Win32_PnPEntity. This is needed at least for some USB cards
534
* that can change the InstanceName whenever being unplugged and
538
hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
539
if (!SUCCEEDED(hr)) {
540
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
541
"from Win32_NetworkAdapter: 0x%x", (int) hr);
542
IWbemClassObject_Release(pObj);
543
IWbemServices_Release(pSvc);
544
if (events->adapter_desc == NULL)
545
return ndis_events_use_desc(events, desc);
546
return 0; /* use Win32_NetworkAdapter::Name */
549
wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
552
len = _snwprintf(query, MAX_QUERY_LEN,
553
L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
554
if (len < 0 || len >= MAX_QUERY_LEN - 1) {
556
IWbemClassObject_Release(pObj);
557
IWbemServices_Release(pSvc);
558
if (events->adapter_desc == NULL)
559
return ndis_events_use_desc(events, desc);
560
return 0; /* use Win32_NetworkAdapter::Name */
564
for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
565
if (vt.bstrVal[pos] == '\\') {
566
if (len >= MAX_QUERY_LEN - 3)
570
query[len++] = vt.bstrVal[pos];
572
query[len++] = L'\'';
575
IWbemClassObject_Release(pObj);
576
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
578
hr = IWbemServices_ExecQuery(pSvc, L"WQL", query,
579
WBEM_FLAG_FORWARD_ONLY |
580
WBEM_FLAG_RETURN_IMMEDIATELY,
582
if (!SUCCEEDED(hr)) {
583
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
584
"Name from Win32_PnPEntity: 0x%x", (int) hr);
585
IWbemServices_Release(pSvc);
586
if (events->adapter_desc == NULL)
587
return ndis_events_use_desc(events, desc);
588
return 0; /* use Win32_NetworkAdapter::Name */
592
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
594
if (!SUCCEEDED(hr) || uReturned == 0) {
595
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
596
"from Win32_PnPEntity: 0x%x", (int) hr);
597
IEnumWbemClassObject_Release(pEnumerator);
598
IWbemServices_Release(pSvc);
599
if (events->adapter_desc == NULL)
600
return ndis_events_use_desc(events, desc);
601
return 0; /* use Win32_NetworkAdapter::Name */
603
IEnumWbemClassObject_Release(pEnumerator);
605
hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
606
if (!SUCCEEDED(hr)) {
607
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
608
"Win32_PnPEntity: 0x%x", (int) hr);
609
IWbemClassObject_Release(pObj);
610
IWbemServices_Release(pSvc);
611
if (events->adapter_desc == NULL)
612
return ndis_events_use_desc(events, desc);
613
return 0; /* use Win32_NetworkAdapter::Name */
616
wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
618
os_free(events->adapter_desc);
619
events->adapter_desc = _wcsdup(vt.bstrVal);
622
IWbemClassObject_Release(pObj);
624
IWbemServices_Release(pSvc);
626
if (events->adapter_desc == NULL)
627
return ndis_events_use_desc(events, desc);
633
struct ndis_events_data *
634
ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
635
const char *ifname, const char *desc)
638
IWbemObjectSink *pSink;
639
struct ndis_events_data *events;
641
events = os_zalloc(sizeof(*events));
642
if (events == NULL) {
643
wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
646
events->ifname = os_strdup(ifname);
647
if (events->ifname == NULL) {
652
if (wmi_refcnt++ == 0) {
653
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
655
wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
656
"returned 0x%x", (int) hr);
663
/* CoInitializeSecurity() must be called once and only once
664
* per process, so let's use wmi_first flag to protect against
668
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
669
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
670
RPC_C_IMP_LEVEL_IMPERSONATE,
671
NULL, EOAC_SECURE_REFS, NULL);
673
wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
674
"- returned 0x%x", (int) hr);
680
hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
681
&IID_IWbemLocator, (LPVOID *) &events->pLoc);
683
wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
690
if (ndis_events_get_adapter(events, ifname, desc) < 0) {
695
wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
696
events->adapter_desc);
698
hr = IWbemLocator_ConnectServer(events->pLoc, L"ROOT\\WMI", NULL, NULL,
699
0, 0, 0, 0, &events->pSvc);
701
wpa_printf(MSG_ERROR, "Could not connect to server - error "
704
os_free(events->adapter_desc);
708
wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
710
ndis_events_constructor(events);
711
pSink = &events->sink;
712
pSink->lpVtbl = &events->sink_vtbl;
713
events->sink_vtbl.QueryInterface = ndis_events_query_interface;
714
events->sink_vtbl.AddRef = ndis_events_add_ref;
715
events->sink_vtbl.Release = ndis_events_release;
716
events->sink_vtbl.Indicate = ndis_events_indicate;
717
events->sink_vtbl.SetStatus = ndis_events_set_status;
719
if (register_async_notification(pSink, events->pSvc) < 0) {
720
wpa_printf(MSG_DEBUG, "Failed to register async "
722
ndis_events_destructor(events);
723
os_free(events->adapter_desc);
728
*read_pipe = events->read_pipe;
729
*event_avail = events->event_avail;