ws2_32: Reimplement SIO_GET_INTERFACE_LIST on top of GetIpAddrTable().

Signed-off-by: Paul Gofman <pgofman@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2021-01-18 14:33:00 +03:00 committed by Alexandre Julliard
parent 085f9fb198
commit 03fcb54c0e
2 changed files with 136 additions and 103 deletions

View File

@ -4489,6 +4489,110 @@ static DWORD server_ioctl_sock( SOCKET s, DWORD code, LPVOID in_buff, DWORD in_s
return NtStatusToWSAError( status );
}
static DWORD get_interface_list(SOCKET s, void *out_buff, DWORD out_size, DWORD *ret_size, DWORD *total_bytes)
{
DWORD size, interface_count = 0, ret;
INTERFACE_INFO *info = out_buff;
PMIB_IPADDRTABLE table = NULL;
DWORD status = 0;
int fd;
if (!out_buff || !ret_size)
return WSAEFAULT;
if ((fd = get_sock_fd(s, 0, NULL)) == -1)
return SOCKET_ERROR;
if ((ret = GetIpAddrTable(NULL, &size, TRUE)) != ERROR_INSUFFICIENT_BUFFER)
{
if (ret != ERROR_NO_DATA)
{
ERR("Unable to get ip address table.\n");
status = WSAEINVAL;
}
goto done;
}
if (!(table = heap_alloc(size)))
{
ERR("No memory.\n");
status = WSAEINVAL;
goto done;
}
if (GetIpAddrTable(table, &size, TRUE) != NO_ERROR)
{
ERR("Unable to get interface table./\n");
status = WSAEINVAL;
goto done;
}
if (table->dwNumEntries * sizeof(INTERFACE_INFO) > out_size)
{
WARN("Buffer too small, dwNumEntries %u, out_size = %u.\n", table->dwNumEntries, out_size);
*ret_size = 0;
status = WSAEFAULT;
goto done;
}
for (; interface_count < table->dwNumEntries; ++interface_count, ++info)
{
unsigned int addr, mask;
struct ifreq if_info;
memset(info, 0, sizeof(*info));
if_info.ifr_ifindex = table->table[interface_count].dwIndex;
if (ioctl(fd, SIOCGIFNAME, &if_info) < 0)
{
ERR("Error obtaining interface name for ifindex %d.\n", if_info.ifr_ifindex);
status = WSAEINVAL;
break;
}
if (ioctl(fd, SIOCGIFFLAGS, &if_info) < 0)
{
ERR("Error obtaining status flags for socket!\n");
status = WSAEINVAL;
break;
}
if (if_info.ifr_flags & IFF_BROADCAST)
info->iiFlags |= WS_IFF_BROADCAST;
#ifdef IFF_POINTOPOINT
if (if_info.ifr_flags & IFF_POINTOPOINT)
info->iiFlags |= WS_IFF_POINTTOPOINT;
#endif
if (if_info.ifr_flags & IFF_LOOPBACK)
info->iiFlags |= WS_IFF_LOOPBACK;
if (if_info.ifr_flags & IFF_UP)
info->iiFlags |= WS_IFF_UP;
if (if_info.ifr_flags & IFF_MULTICAST)
info->iiFlags |= WS_IFF_MULTICAST;
addr = table->table[interface_count].dwAddr;
mask = table->table[interface_count].dwMask;
info->iiAddress.AddressIn.sin_family = WS_AF_INET;
info->iiAddress.AddressIn.sin_port = 0;
info->iiAddress.AddressIn.sin_addr.WS_s_addr = addr;
info->iiNetmask.AddressIn.sin_family = WS_AF_INET;
info->iiNetmask.AddressIn.sin_port = 0;
info->iiNetmask.AddressIn.sin_addr.WS_s_addr = mask;
if (if_info.ifr_flags & IFF_BROADCAST)
{
info->iiBroadcastAddress.AddressIn.sin_family = WS_AF_INET;
info->iiBroadcastAddress.AddressIn.sin_port = 0;
info->iiBroadcastAddress.AddressIn.sin_addr.WS_s_addr = addr | ~mask;
}
}
done:
heap_free(table);
*total_bytes = sizeof(INTERFACE_INFO) * interface_count;
release_sock_fd(s, fd);
return status;
}
/**********************************************************************
* WSAIoctl (WS2_32.50)
*
@ -4584,111 +4688,9 @@ INT WINAPI WSAIoctl(SOCKET s, DWORD code, LPVOID in_buff, DWORD in_size, LPVOID
case WS_SIO_GET_INTERFACE_LIST:
{
INTERFACE_INFO* intArray = out_buff;
DWORD size, numInt = 0, apiReturn;
TRACE("-> SIO_GET_INTERFACE_LIST request\n");
if (!out_buff || !ret_size)
{
SetLastError(WSAEFAULT);
return SOCKET_ERROR;
}
fd = get_sock_fd( s, 0, NULL );
if (fd == -1) return SOCKET_ERROR;
apiReturn = GetAdaptersInfo(NULL, &size);
if (apiReturn == ERROR_BUFFER_OVERFLOW)
{
PIP_ADAPTER_INFO table = HeapAlloc(GetProcessHeap(),0,size);
if (table)
{
if (GetAdaptersInfo(table, &size) == NO_ERROR)
{
PIP_ADAPTER_INFO ptr;
for (ptr = table, numInt = 0; ptr; ptr = ptr->Next)
{
unsigned int addr, mask, bcast;
struct ifreq ifInfo;
/* Skip interfaces without an IPv4 address. */
if (ptr->IpAddressList.IpAddress.String[0] == '\0')
continue;
if ((numInt + 1) * sizeof(INTERFACE_INFO) > out_size)
{
WARN("Buffer too small = %u, out_size = %u\n", numInt + 1, out_size);
status = WSAEFAULT;
if (ret_size) *ret_size = 0;
break;
}
/* Socket Status Flags */
lstrcpynA(ifInfo.ifr_name, ptr->AdapterName, IFNAMSIZ);
if (ioctl(fd, SIOCGIFFLAGS, &ifInfo) < 0)
{
ERR("Error obtaining status flags for socket!\n");
status = WSAEINVAL;
break;
}
else
{
/* set flags; the values of IFF_* are not the same
under Linux and Windows, therefore must generate
new flags */
intArray->iiFlags = 0;
if (ifInfo.ifr_flags & IFF_BROADCAST)
intArray->iiFlags |= WS_IFF_BROADCAST;
#ifdef IFF_POINTOPOINT
if (ifInfo.ifr_flags & IFF_POINTOPOINT)
intArray->iiFlags |= WS_IFF_POINTTOPOINT;
#endif
if (ifInfo.ifr_flags & IFF_LOOPBACK)
intArray->iiFlags |= WS_IFF_LOOPBACK;
if (ifInfo.ifr_flags & IFF_UP)
intArray->iiFlags |= WS_IFF_UP;
if (ifInfo.ifr_flags & IFF_MULTICAST)
intArray->iiFlags |= WS_IFF_MULTICAST;
}
addr = inet_addr(ptr->IpAddressList.IpAddress.String);
mask = inet_addr(ptr->IpAddressList.IpMask.String);
bcast = addr | ~mask;
intArray->iiAddress.AddressIn.sin_family = WS_AF_INET;
intArray->iiAddress.AddressIn.sin_port = 0;
intArray->iiAddress.AddressIn.sin_addr.WS_s_addr = addr;
intArray->iiNetmask.AddressIn.sin_family = WS_AF_INET;
intArray->iiNetmask.AddressIn.sin_port = 0;
intArray->iiNetmask.AddressIn.sin_addr.WS_s_addr = mask;
intArray->iiBroadcastAddress.AddressIn.sin_family = WS_AF_INET;
intArray->iiBroadcastAddress.AddressIn.sin_port = 0;
intArray->iiBroadcastAddress.AddressIn.sin_addr.WS_s_addr = bcast;
intArray++;
numInt++;
}
}
else
{
ERR("Unable to get interface table!\n");
status = WSAEINVAL;
}
HeapFree(GetProcessHeap(),0,table);
}
else status = WSAEINVAL;
}
else if (apiReturn != ERROR_NO_DATA)
{
ERR("Unable to get interface table!\n");
status = WSAEINVAL;
}
/* Calculate the size of the array being returned */
total = sizeof(INTERFACE_INFO) * numInt;
release_sock_fd( s, fd );
status = get_interface_list(s, out_buff, out_size, ret_size, &total);
break;
}

View File

@ -10516,6 +10516,9 @@ static void test_WSCGetProviderPath(void)
static void test_wsaioctl(void)
{
unsigned int i, count;
INTERFACE_INFO *info;
BOOL loopback_found;
char buffer[4096];
DWORD size;
SOCKET s;
@ -10529,12 +10532,40 @@ static void test_wsaioctl(void)
ok(!ret, "Got unexpected ret %d.\n", ret);
ok(size && size != 0xdeadbeef && !(size % sizeof(INTERFACE_INFO)), "Got unexpected size %u.\n", size);
info = (INTERFACE_INFO *)buffer;
count = size / sizeof(INTERFACE_INFO);
loopback_found = FALSE;
for (i = 0; i < count; ++i)
{
if (info[i].iiFlags & IFF_LOOPBACK)
loopback_found = TRUE;
ok(info[i].iiAddress.AddressIn.sin_family == AF_INET, "Got unexpected sin_family %#x.\n",
info[i].iiAddress.AddressIn.sin_family);
ok(info[i].iiNetmask.AddressIn.sin_family == AF_INET, "Got unexpected sin_family %#x.\n",
info[i].iiNetmask.AddressIn.sin_family);
ok(info[i].iiBroadcastAddress.AddressIn.sin_family
== (info[i].iiFlags & IFF_BROADCAST) ? AF_INET : 0, "Got unexpected sin_family %#x.\n",
info[i].iiBroadcastAddress.AddressIn.sin_family);
ok(info[i].iiAddress.AddressIn.sin_addr.S_un.S_addr, "Got zero iiAddress.\n");
ok(info[i].iiNetmask.AddressIn.sin_addr.S_un.S_addr, "Got zero iiNetmask.\n");
ok((info[i].iiFlags & IFF_BROADCAST) ? info[i].iiBroadcastAddress.AddressIn.sin_addr.S_un.S_addr
: !info[i].iiBroadcastAddress.AddressIn.sin_addr.S_un.S_addr,
"Got unexpected iiBroadcastAddress %s.\n", inet_ntoa(info[i].iiBroadcastAddress.AddressIn.sin_addr));
}
ok(loopback_found, "Loopback interface not found.\n");
size = 0xdeadbeef;
ret = WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, sizeof(INTERFACE_INFO) - 1, &size, NULL, NULL);
ok(ret == -1, "Got unexpected ret %d.\n", ret);
ok(WSAGetLastError() == WSAEFAULT, "Got unexpected error %d.\n", WSAGetLastError());
ok(!size, "Got unexpected size %u.\n", size);
ret = WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, sizeof(buffer), NULL, NULL, NULL);
ok(ret == -1, "Got unexpected ret %d.\n", ret);
ok(WSAGetLastError() == WSAEFAULT, "Got unexpected error %d.\n", WSAGetLastError());
closesocket(s);
}