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