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