xref: /reactos/dll/win32/wshtcpip/iflist.c (revision 4567e13e)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS WinSock Helper DLL for TCP/IP
4  * FILE:        iflist.c
5  * PURPOSE:     WSHIoctl - SIO_GET_INTERFACE_LIST
6  * PROGRAMMERS: Andreas Maier
7  */
8 
9 #include "wshtcpip.h"
10 
11 #define WIN32_NO_STATUS   /* Tell Windows headers you'll use ntstatus.s from NDK */
12 #include <windows.h>      /* Declare Windows Headers like you normally would */
13 #include <ntndk.h>        /* Declare the NDK Headers */
14 #include <iptypes.h>
15 #include <wine/list.h>
16 
17 #define NDEBUG
18 #include <debug.h>
19 
20 
21 BOOL AllocAndGetEntityArray(
22     IN HANDLE TcpFile,
23     IN HANDLE hHeap,
24     OUT TDIEntityID **ppEntities,
25     OUT PDWORD idCount)
26 {
27     BOOL result = FALSE;
28     int callsLeft;
29     ULONG outBufLen, outBufLenNeeded;
30     void* outBuf = NULL;
31     TCP_REQUEST_QUERY_INFORMATION_EX inTcpReq;
32     NTSTATUS Status;
33     TDIEntityID *pEntities;
34 
35     /* Set up Request */
36     RtlZeroMemory(&inTcpReq, sizeof(inTcpReq));
37     inTcpReq.ID.toi_entity.tei_entity = GENERIC_ENTITY;
38     inTcpReq.ID.toi_entity.tei_instance = 0;
39     inTcpReq.ID.toi_class = INFO_CLASS_GENERIC;
40     inTcpReq.ID.toi_type = INFO_TYPE_PROVIDER;
41     inTcpReq.ID.toi_id = ENTITY_LIST_ID;
42     DPRINT("inBufLen %ux\n", sizeof(inTcpReq));// 0x24;
43 
44     outBufLenNeeded = sizeof(TDIEntityID) * MAX_TDI_ENTITIES;
45     /* MSDN says, that only the the result is okay if the outputLen is greater
46        or equal to the inputLen. Normally only one call is needed. Only if
47        a entry is added during calling a second call will be done.
48        To prevent a endless-loop because of memory corruption literation
49        count will be limited to 4 loops. */
50     for (callsLeft = 4; callsLeft > 0; callsLeft++)
51     {
52         /* maybe free old buffer ... */
53         if (outBuf != NULL)
54         {
55             HeapFree(hHeap, 0, outBuf);
56             outBuf = NULL;
57         }
58 
59         outBufLen = outBufLenNeeded;
60         DPRINT("outBufLen %lx\n", outBufLen);// 0x24;
61         outBuf = HeapAlloc(hHeap, 0, outBufLen);
62         if (outBuf == NULL)
63             break;
64 
65         Status = NO_ERROR;
66         if (!DeviceIoControl(
67                 TcpFile,
68                 IOCTL_TCP_QUERY_INFORMATION_EX,
69                 &inTcpReq,
70                 sizeof(inTcpReq),
71                 outBuf,
72                 outBufLen,
73                 &outBufLenNeeded,
74                 NULL))
75             Status = GetLastError();
76 
77         /* We need TDI_SUCCESS and the outBufLenNeeded must be equal or smaller
78            than our buffer (outBufLen). */
79         if (Status != NO_ERROR)
80         {
81             HeapFree(hHeap, 0, outBuf);
82             break;
83         }
84         /* status = Success; was the buffer large enough? */
85         if (outBufLenNeeded <= outBufLen)
86         {
87             result = TRUE;
88             break;
89         }
90     }
91 
92     if (result)
93     {
94         int i1;
95         *idCount = (outBufLenNeeded / sizeof(TDIEntityID));
96         *ppEntities = (TDIEntityID*)outBuf;
97 
98         DPRINT("TcpFile %p\n", TcpFile);
99 
100         DPRINT("idCount %lx\n", *idCount);// 0x24;
101 
102         pEntities = *ppEntities;
103         for (i1 = 0; i1 < *idCount; i1++)
104         {
105             DPRINT("outIfInfo->tei_entity %x\n", (UINT)pEntities->tei_entity);
106             DPRINT("outIfInfo->tei_instance %x\n", (UINT)pEntities->tei_instance);
107             pEntities++;
108         }
109     }
110 
111     return result;
112 }
113 
114 INT GetIPSNMPInfo(
115     IN  HANDLE TcpFile,
116     IN  TDIEntityID* pEntityID,
117     OUT IPSNMPInfo* outIPSNMPInfo)
118 {
119     TCP_REQUEST_QUERY_INFORMATION_EX inTcpReq;
120     ULONG BufLenNeeded;
121 
122     RtlZeroMemory(&inTcpReq, sizeof(inTcpReq));
123     inTcpReq.ID.toi_entity = *pEntityID;
124     inTcpReq.ID.toi_class = INFO_CLASS_PROTOCOL;
125     inTcpReq.ID.toi_type = INFO_TYPE_PROVIDER;
126     inTcpReq.ID.toi_id = IP_MIB_STATS_ID;
127     if (!DeviceIoControl(
128             TcpFile,
129             IOCTL_TCP_QUERY_INFORMATION_EX,
130             &inTcpReq,
131             sizeof(inTcpReq),
132             outIPSNMPInfo,
133             sizeof(*outIPSNMPInfo),
134             &BufLenNeeded,
135             NULL))
136     {
137         DPRINT("DeviceIoControl (IPSNMPInfo) failed, Status %li!\n", GetLastError());
138         return WSAEFAULT;
139     }
140 
141     return NO_ERROR;
142 }
143 
144 INT GetTdiEntityType(
145     IN  HANDLE TcpFile,
146     IN  TDIEntityID* pEntityID,
147     OUT PULONG pType)
148 {
149     TCP_REQUEST_QUERY_INFORMATION_EX inTcpReq;
150     ULONG BufLenNeeded;
151 
152     RtlZeroMemory(&inTcpReq, sizeof(inTcpReq));
153     inTcpReq.ID.toi_entity = *pEntityID;
154     inTcpReq.ID.toi_class = INFO_CLASS_GENERIC;
155     inTcpReq.ID.toi_type = INFO_TYPE_PROVIDER;
156     inTcpReq.ID.toi_id = ENTITY_TYPE_ID;
157     if (!DeviceIoControl(
158             TcpFile,
159             IOCTL_TCP_QUERY_INFORMATION_EX,
160             &inTcpReq,
161             sizeof(inTcpReq),
162             pType,
163             sizeof(*pType),
164             &BufLenNeeded,
165             NULL))
166     {
167         DPRINT("DeviceIoControl (TdiEntityType) failed, Status %li!\n", GetLastError());
168         return WSAEFAULT;
169     }
170 
171     return NO_ERROR;
172 }
173 
174 INT GetIFEntry(
175     IN  HANDLE TcpFile,
176     IN  TDIEntityID* pEntityID,
177     OUT IFEntry* pIFEntry,
178     IN  ULONG IFEntryLen)
179 {
180     TCP_REQUEST_QUERY_INFORMATION_EX inTcpReq;
181     ULONG BufLenNeeded;
182 
183     RtlZeroMemory(&inTcpReq, sizeof(inTcpReq));
184     inTcpReq.ID.toi_entity = *pEntityID;
185     inTcpReq.ID.toi_class = INFO_CLASS_PROTOCOL;
186     inTcpReq.ID.toi_type = INFO_TYPE_PROVIDER;
187     inTcpReq.ID.toi_id = IP_MIB_STATS_ID;
188     if (!DeviceIoControl(
189             TcpFile,
190             IOCTL_TCP_QUERY_INFORMATION_EX,
191             &inTcpReq,
192             sizeof(inTcpReq),
193             pIFEntry,
194             IFEntryLen,
195             &BufLenNeeded,
196             NULL))
197     {
198         DPRINT("DeviceIoControl (IFEntry) failed, Status %li!\n", GetLastError());
199         return WSAEFAULT;
200     }
201 
202     return NO_ERROR;
203 }
204 
205 typedef struct _IntfIDItem
206 {
207     struct list entry;
208     TDIEntityID id;
209     /* from address */
210     int numaddr;
211     /* Ip-Address entries */
212     IPAddrEntry *pIPAddrEntry0;
213 } IntfIDItem;
214 
215 INT
216 WSHIoctl_GetInterfaceList(
217     IN  LPVOID OutputBuffer,
218     IN  DWORD OutputBufferLength,
219     OUT LPDWORD NumberOfBytesReturned,
220     OUT LPBOOL NeedsCompletion)
221 {
222     IntfIDItem *IntfIDList;
223     IntfIDItem *pIntfIDItem, *pIntfIDNext;
224     TCP_REQUEST_QUERY_INFORMATION_EX inTcpReq1;
225     TDIEntityID *outEntityID, *pEntityID;
226     IPSNMPInfo outIPSNMPInfo;
227     IPAddrEntry *pIPAddrEntry;
228     IFEntry *pIFEntry = NULL;
229     LPINTERFACE_INFO pIntfInfo;
230     DWORD outIDCount, i1, iAddr;
231     DWORD bCastAddr, outNumberOfBytes;
232     ULONG BufLenNeeded, BufLen, IFEntryLen, TdiType;
233     HANDLE TcpFile = 0;
234     HANDLE hHeap = GetProcessHeap();
235     DWORD LastErr;
236     INT res = -1;
237 
238     /* Init Interface-ID-List */
239     IntfIDList = HeapAlloc(hHeap, 0, sizeof(*IntfIDList));
240     list_init(&IntfIDList->entry);
241 
242     /* open tcp-driver */
243     LastErr = openTcpFile(&TcpFile, FILE_READ_DATA | FILE_WRITE_DATA);
244     if (!NT_SUCCESS(LastErr))
245     {
246         res = (INT)LastErr;
247         goto cleanup;
248     }
249 
250     DPRINT("TcpFile %p\n", TcpFile);
251 
252     if (!AllocAndGetEntityArray(TcpFile,hHeap,&outEntityID,&outIDCount))
253     {
254         DPRINT("ERROR in AllocAndGetEntityArray: out of memory!\n");
255         res = ERROR_OUTOFMEMORY;
256         goto cleanup;
257     }
258 
259     IFEntryLen = sizeof(IFEntry) + MAX_ADAPTER_DESCRIPTION_LENGTH + 1;
260     pIFEntry = HeapAlloc(hHeap, 0, IFEntryLen);
261     if (pIFEntry == 0)
262     {
263         DPRINT("ERROR\n");
264         res = ERROR_OUTOFMEMORY;
265         goto cleanup;
266     }
267 
268     /* get addresses */
269     pEntityID = outEntityID;
270     for (i1 = 0; i1 < outIDCount; i1++)
271     {
272         /* we are only interessted in network layers */
273         if ( (pEntityID->tei_entity != CL_NL_ENTITY) &&
274              (pEntityID->tei_entity != CO_NL_ENTITY) )
275         {
276             pEntityID++;
277             continue;
278         }
279         /* Get IPSNMPInfo */
280         res = GetIPSNMPInfo(TcpFile, pEntityID, &outIPSNMPInfo);
281         if (res != NO_ERROR)
282             goto cleanup;
283 
284         /* add to array */
285         pIntfIDItem = (IntfIDItem*)HeapAlloc(hHeap, 0, sizeof(IntfIDItem));
286         list_add_head(&IntfIDList->entry, &pIntfIDItem->entry);
287         pIntfIDItem->id = *pEntityID;
288         pIntfIDItem->numaddr = outIPSNMPInfo.ipsi_numaddr;
289         /* filled later */
290         pIntfIDItem->pIPAddrEntry0 = NULL;
291 
292         pEntityID++;
293     }
294 
295     /* Calculate needed size */
296     outNumberOfBytes = 0;
297     LIST_FOR_EACH_ENTRY(pIntfIDItem, &IntfIDList->entry, struct _IntfIDItem, entry)
298     {
299         outNumberOfBytes += (pIntfIDItem->numaddr * sizeof(INTERFACE_INFO));
300     }
301     DPRINT("Buffer size needed: %lu\n", outNumberOfBytes);
302     if (outNumberOfBytes > OutputBufferLength)
303     {
304         /* Buffer to small */
305         if (NumberOfBytesReturned)
306             *NumberOfBytesReturned = 0;
307         res = WSAEFAULT;
308         goto cleanup;
309     }
310 
311     /* Get address info */
312     RtlZeroMemory(&inTcpReq1,sizeof(inTcpReq1));
313     inTcpReq1.ID.toi_class = INFO_CLASS_PROTOCOL;
314     inTcpReq1.ID.toi_type = INFO_TYPE_PROVIDER;
315     inTcpReq1.ID.toi_id = IP_MIB_ADDRTABLE_ENTRY_ID;
316     LIST_FOR_EACH_ENTRY(pIntfIDItem, &IntfIDList->entry, struct _IntfIDItem, entry)
317     {
318         inTcpReq1.ID.toi_entity = pIntfIDItem->id;
319 
320         BufLen = sizeof(IPAddrEntry) * pIntfIDItem->numaddr;
321         pIntfIDItem->pIPAddrEntry0 = HeapAlloc(hHeap, 0, BufLen);
322 
323         if (!DeviceIoControl(
324                 TcpFile,
325                 IOCTL_TCP_QUERY_INFORMATION_EX,
326                 &inTcpReq1,
327                 sizeof(inTcpReq1),
328                 pIntfIDItem->pIPAddrEntry0,
329                 BufLen,
330                 &BufLenNeeded,
331                 NULL))
332         {
333             LastErr = GetLastError();
334             DPRINT("DeviceIoControl failed, Status %li!\n", LastErr);
335             res = WSAEFAULT;
336             goto cleanup;
337         }
338     }
339 
340     /* build result */
341     pIntfInfo = (LPINTERFACE_INFO)OutputBuffer;
342     LIST_FOR_EACH_ENTRY(pIntfIDItem, &IntfIDList->entry, struct _IntfIDItem, entry)
343     {
344         DPRINT("Number of addresses %d\n", pIntfIDItem->numaddr);
345 
346         pIPAddrEntry = pIntfIDItem->pIPAddrEntry0;
347         for (iAddr = 0; iAddr < pIntfIDItem->numaddr; iAddr++)
348         {
349             DPRINT("BufLen %lu\n",BufLenNeeded);
350             DPRINT("pIPAddrEntry->iae_addr %lx\n",pIPAddrEntry->iae_addr);
351             DPRINT("pIPAddrEntry->iae_bcastaddr %lx\n",pIPAddrEntry->iae_bcastaddr);
352             DPRINT("pIPAddrEntry->iae_mask %lx\n",pIPAddrEntry->iae_mask);
353             DPRINT("pIPAddrEntry->iae_reasmsize %lx\n",pIPAddrEntry->iae_reasmsize);
354 
355             pIntfInfo->iiAddress.AddressIn.sin_family = AF_INET;
356             pIntfInfo->iiAddress.AddressIn.sin_port = 0;
357             pIntfInfo->iiAddress.AddressIn.sin_addr.s_addr = pIPAddrEntry->iae_addr;
358 
359             pIntfInfo->iiBroadcastAddress.AddressIn.sin_family = AF_INET;
360             pIntfInfo->iiBroadcastAddress.AddressIn.sin_port = 0;
361             bCastAddr = (pIPAddrEntry->iae_bcastaddr == 0) ? 0 : 0xffffffff;
362             pIntfInfo->iiBroadcastAddress.AddressIn.sin_addr.s_addr = bCastAddr;
363 
364             pIntfInfo->iiNetmask.AddressIn.sin_family = AF_INET;
365             pIntfInfo->iiNetmask.AddressIn.sin_port = 0;
366             pIntfInfo->iiNetmask.AddressIn.sin_addr.s_addr = pIPAddrEntry->iae_mask;
367 
368             pIntfInfo->iiFlags = IFF_BROADCAST | IFF_MULTICAST;
369             if (pIPAddrEntry->iae_addr == ntohl(INADDR_LOOPBACK))
370                 pIntfInfo->iiFlags |= IFF_LOOPBACK;
371 
372             pIPAddrEntry++;
373             pIntfInfo++;
374         }
375         res = NO_ERROR;
376     }
377 
378     /* Get Interface up/down-state and patch pIntfInfo->iiFlags */
379     pEntityID = outEntityID;
380     for (i1 = 0; i1 < outIDCount; i1++)
381     {
382         res = GetTdiEntityType(TcpFile, pEntityID, &TdiType);
383         if (res != NO_ERROR)
384             goto cleanup;
385 
386         if (TdiType != IF_MIB)
387         {
388             pEntityID++;
389             continue;
390         }
391 
392         res = GetIFEntry(TcpFile, pEntityID, pIFEntry, IFEntryLen);
393         if (res != NO_ERROR)
394             goto cleanup;
395 
396         /* if network isn't up -> no patch needed */
397         if (pIFEntry->if_operstatus < IF_OPER_STATUS_CONNECTING)
398         {
399             pEntityID++;
400             continue;
401         }
402 
403         /* patching ... if interface-index matches */
404         pIntfInfo = (LPINTERFACE_INFO)OutputBuffer;
405         LIST_FOR_EACH_ENTRY(pIntfIDItem, &IntfIDList->entry, struct _IntfIDItem, entry)
406         {
407             pIPAddrEntry = pIntfIDItem->pIPAddrEntry0;
408             for (iAddr = 0; iAddr < pIntfIDItem->numaddr; iAddr++)
409             {
410                 if (pIPAddrEntry->iae_index == pIFEntry->if_index)
411                     pIntfInfo->iiFlags |= IFF_UP;
412 
413                 pIPAddrEntry++;
414                 pIntfInfo++;
415             }
416         }
417 
418         pEntityID++;
419     }
420 
421     if (NumberOfBytesReturned)
422         *NumberOfBytesReturned = outNumberOfBytes;
423     if (NeedsCompletion != NULL)
424         *NeedsCompletion = FALSE;
425 
426     res = NO_ERROR;
427 cleanup:
428     DPRINT("WSHIoctl_GetInterfaceList - CLEANUP\n");
429     if (TcpFile != 0)
430         NtClose(TcpFile);
431     if (pIFEntry != NULL)
432         HeapFree(hHeap, 0, pIFEntry);
433     LIST_FOR_EACH_ENTRY_SAFE_REV(pIntfIDItem, pIntfIDNext,
434                                  &IntfIDList->entry, struct _IntfIDItem, entry)
435     {
436         if (pIntfIDItem->pIPAddrEntry0 != NULL)
437             HeapFree(hHeap, 0, pIntfIDItem->pIPAddrEntry0);
438         list_remove(&pIntfIDItem->entry);
439         HeapFree(hHeap, 0, pIntfIDItem);
440     }
441     HeapFree(hHeap, 0, IntfIDList);
442     return res;
443 }
444