/**
 * WinPR: Windows Portable Runtime
 * Windows Sockets (Winsock)
 *
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <winpr/config.h>

#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/synch.h>

#include <winpr/winsock.h>

#ifdef WINPR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef WINPR_HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#ifdef WINPR_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif

#ifndef _WIN32
#include <fcntl.h>
#endif

#ifdef __APPLE__
#define WSAIOCTL_IFADDRS
#include <ifaddrs.h>
#endif

/**
 * ws2_32.dll:
 *
 * __WSAFDIsSet
 * accept
 * bind
 * closesocket
 * connect
 * freeaddrinfo
 * FreeAddrInfoEx
 * FreeAddrInfoExW
 * FreeAddrInfoW
 * getaddrinfo
 * GetAddrInfoExA
 * GetAddrInfoExCancel
 * GetAddrInfoExOverlappedResult
 * GetAddrInfoExW
 * GetAddrInfoW
 * gethostbyaddr
 * gethostbyname
 * gethostname
 * GetHostNameW
 * getnameinfo
 * GetNameInfoW
 * getpeername
 * getprotobyname
 * getprotobynumber
 * getservbyname
 * getservbyport
 * getsockname
 * getsockopt
 * htonl
 * htons
 * inet_addr
 * inet_ntoa
 * inet_ntop
 * inet_pton
 * InetNtopW
 * InetPtonW
 * ioctlsocket
 * listen
 * ntohl
 * ntohs
 * recv
 * recvfrom
 * select
 * send
 * sendto
 * SetAddrInfoExA
 * SetAddrInfoExW
 * setsockopt
 * shutdown
 * socket
 * WahCloseApcHelper
 * WahCloseHandleHelper
 * WahCloseNotificationHandleHelper
 * WahCloseSocketHandle
 * WahCloseThread
 * WahCompleteRequest
 * WahCreateHandleContextTable
 * WahCreateNotificationHandle
 * WahCreateSocketHandle
 * WahDestroyHandleContextTable
 * WahDisableNonIFSHandleSupport
 * WahEnableNonIFSHandleSupport
 * WahEnumerateHandleContexts
 * WahInsertHandleContext
 * WahNotifyAllProcesses
 * WahOpenApcHelper
 * WahOpenCurrentThread
 * WahOpenHandleHelper
 * WahOpenNotificationHandleHelper
 * WahQueueUserApc
 * WahReferenceContextByHandle
 * WahRemoveHandleContext
 * WahWaitForNotification
 * WahWriteLSPEvent
 * WEP
 * WPUCompleteOverlappedRequest
 * WPUGetProviderPathEx
 * WSAAccept
 * WSAAddressToStringA
 * WSAAddressToStringW
 * WSAAdvertiseProvider
 * WSAAsyncGetHostByAddr
 * WSAAsyncGetHostByName
 * WSAAsyncGetProtoByName
 * WSAAsyncGetProtoByNumber
 * WSAAsyncGetServByName
 * WSAAsyncGetServByPort
 * WSAAsyncSelect
 * WSACancelAsyncRequest
 * WSACancelBlockingCall
 * WSACleanup
 * WSACloseEvent
 * WSAConnect
 * WSAConnectByList
 * WSAConnectByNameA
 * WSAConnectByNameW
 * WSACreateEvent
 * WSADuplicateSocketA
 * WSADuplicateSocketW
 * WSAEnumNameSpaceProvidersA
 * WSAEnumNameSpaceProvidersExA
 * WSAEnumNameSpaceProvidersExW
 * WSAEnumNameSpaceProvidersW
 * WSAEnumNetworkEvents
 * WSAEnumProtocolsA
 * WSAEnumProtocolsW
 * WSAEventSelect
 * WSAGetLastError
 * WSAGetOverlappedResult
 * WSAGetQOSByName
 * WSAGetServiceClassInfoA
 * WSAGetServiceClassInfoW
 * WSAGetServiceClassNameByClassIdA
 * WSAGetServiceClassNameByClassIdW
 * WSAHtonl
 * WSAHtons
 * WSAInstallServiceClassA
 * WSAInstallServiceClassW
 * WSAIoctl
 * WSAIsBlocking
 * WSAJoinLeaf
 * WSALookupServiceBeginA
 * WSALookupServiceBeginW
 * WSALookupServiceEnd
 * WSALookupServiceNextA
 * WSALookupServiceNextW
 * WSANSPIoctl
 * WSANtohl
 * WSANtohs
 * WSAPoll
 * WSAProviderCompleteAsyncCall
 * WSAProviderConfigChange
 * WSApSetPostRoutine
 * WSARecv
 * WSARecvDisconnect
 * WSARecvFrom
 * WSARemoveServiceClass
 * WSAResetEvent
 * WSASend
 * WSASendDisconnect
 * WSASendMsg
 * WSASendTo
 * WSASetBlockingHook
 * WSASetEvent
 * WSASetLastError
 * WSASetServiceA
 * WSASetServiceW
 * WSASocketA
 * WSASocketW
 * WSAStartup
 * WSAStringToAddressA
 * WSAStringToAddressW
 * WSAUnadvertiseProvider
 * WSAUnhookBlockingHook
 * WSAWaitForMultipleEvents
 * WSCDeinstallProvider
 * WSCDeinstallProviderEx
 * WSCEnableNSProvider
 * WSCEnumProtocols
 * WSCEnumProtocolsEx
 * WSCGetApplicationCategory
 * WSCGetApplicationCategoryEx
 * WSCGetProviderInfo
 * WSCGetProviderPath
 * WSCInstallNameSpace
 * WSCInstallNameSpaceEx
 * WSCInstallNameSpaceEx2
 * WSCInstallProvider
 * WSCInstallProviderAndChains
 * WSCInstallProviderEx
 * WSCSetApplicationCategory
 * WSCSetApplicationCategoryEx
 * WSCSetProviderInfo
 * WSCUnInstallNameSpace
 * WSCUnInstallNameSpaceEx2
 * WSCUpdateProvider
 * WSCUpdateProviderEx
 * WSCWriteNameSpaceOrder
 * WSCWriteProviderOrder
 * WSCWriteProviderOrderEx
 */

#ifdef _WIN32

#if (_WIN32_WINNT < 0x0600)

PCSTR winpr_inet_ntop(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize)
{
	if (Family == AF_INET)
	{
		struct sockaddr_in in = WINPR_C_ARRAY_INIT;

		in.sin_family = AF_INET;
		memcpy(&in.sin_addr, pAddr, sizeof(struct in_addr));
		getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in), pStringBuf, StringBufSize,
		            nullptr, 0, NI_NUMERICHOST);
		return pStringBuf;
	}
	else if (Family == AF_INET6)
	{
		struct sockaddr_in6 in = WINPR_C_ARRAY_INIT;

		in.sin6_family = AF_INET6;
		memcpy(&in.sin6_addr, pAddr, sizeof(struct in_addr6));
		getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in6), pStringBuf, StringBufSize,
		            nullptr, 0, NI_NUMERICHOST);
		return pStringBuf;
	}

	return nullptr;
}

INT winpr_inet_pton(INT Family, PCSTR pszAddrString, PVOID pAddrBuf)
{
	SOCKADDR_STORAGE addr;
	int addr_len = sizeof(addr);

	if ((Family != AF_INET) && (Family != AF_INET6))
		return -1;

	if (WSAStringToAddressA((char*)pszAddrString, Family, nullptr, (struct sockaddr*)&addr,
	                        &addr_len) != 0)
		return 0;

	if (Family == AF_INET)
	{
		memcpy(pAddrBuf, &((struct sockaddr_in*)&addr)->sin_addr, sizeof(struct in_addr));
	}
	else if (Family == AF_INET6)
	{
		memcpy(pAddrBuf, &((struct sockaddr_in6*)&addr)->sin6_addr, sizeof(struct in6_addr));
	}

	return 1;
}

#endif /* (_WIN32_WINNT < 0x0600) */

#else /* _WIN32 */

#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <net/if.h>

#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

int WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
{
	WINPR_ASSERT(lpWSAData);

	ZeroMemory(lpWSAData, sizeof(WSADATA));
	lpWSAData->wVersion = wVersionRequired;
	lpWSAData->wHighVersion = MAKEWORD(2, 2);
	return 0; /* success */
}

int WSACleanup(void)
{
	return 0; /* success */
}

void WSASetLastError(int iError)
{
	switch (iError)
	{
		/* Base error codes */
		case WSAEINTR:
			errno = EINTR;
			break;

		case WSAEBADF:
			errno = EBADF;
			break;

		case WSAEACCES:
			errno = EACCES;
			break;

		case WSAEFAULT:
			errno = EFAULT;
			break;

		case WSAEINVAL:
			errno = EINVAL;
			break;

		case WSAEMFILE:
			errno = EMFILE;
			break;

			/* BSD sockets error codes */

		case WSAEWOULDBLOCK:
			errno = EWOULDBLOCK;
			break;

		case WSAEINPROGRESS:
			errno = EINPROGRESS;
			break;

		case WSAEALREADY:
			errno = EALREADY;
			break;

		case WSAENOTSOCK:
			errno = ENOTSOCK;
			break;

		case WSAEDESTADDRREQ:
			errno = EDESTADDRREQ;
			break;

		case WSAEMSGSIZE:
			errno = EMSGSIZE;
			break;

		case WSAEPROTOTYPE:
			errno = EPROTOTYPE;
			break;

		case WSAENOPROTOOPT:
			errno = ENOPROTOOPT;
			break;

		case WSAEPROTONOSUPPORT:
			errno = EPROTONOSUPPORT;
			break;

		case WSAESOCKTNOSUPPORT:
			errno = ESOCKTNOSUPPORT;
			break;

		case WSAEOPNOTSUPP:
			errno = EOPNOTSUPP;
			break;

		case WSAEPFNOSUPPORT:
			errno = EPFNOSUPPORT;
			break;

		case WSAEAFNOSUPPORT:
			errno = EAFNOSUPPORT;
			break;

		case WSAEADDRINUSE:
			errno = EADDRINUSE;
			break;

		case WSAEADDRNOTAVAIL:
			errno = EADDRNOTAVAIL;
			break;

		case WSAENETDOWN:
			errno = ENETDOWN;
			break;

		case WSAENETUNREACH:
			errno = ENETUNREACH;
			break;

		case WSAENETRESET:
			errno = ENETRESET;
			break;

		case WSAECONNABORTED:
			errno = ECONNABORTED;
			break;

		case WSAECONNRESET:
			errno = ECONNRESET;
			break;

		case WSAENOBUFS:
			errno = ENOBUFS;
			break;

		case WSAEISCONN:
			errno = EISCONN;
			break;

		case WSAENOTCONN:
			errno = ENOTCONN;
			break;

		case WSAESHUTDOWN:
			errno = ESHUTDOWN;
			break;

		case WSAETOOMANYREFS:
			errno = ETOOMANYREFS;
			break;

		case WSAETIMEDOUT:
			errno = ETIMEDOUT;
			break;

		case WSAECONNREFUSED:
			errno = ECONNREFUSED;
			break;

		case WSAELOOP:
			errno = ELOOP;
			break;

		case WSAENAMETOOLONG:
			errno = ENAMETOOLONG;
			break;

		case WSAEHOSTDOWN:
			errno = EHOSTDOWN;
			break;

		case WSAEHOSTUNREACH:
			errno = EHOSTUNREACH;
			break;

		case WSAENOTEMPTY:
			errno = ENOTEMPTY;
			break;
#ifdef EPROCLIM

		case WSAEPROCLIM:
			errno = EPROCLIM;
			break;
#endif

		case WSAEUSERS:
			errno = EUSERS;
			break;

		case WSAEDQUOT:
			errno = EDQUOT;
			break;

		case WSAESTALE:
			errno = ESTALE;
			break;

		case WSAEREMOTE:
			errno = EREMOTE;
			break;
		default:
			break;
	}
}

int WSAGetLastError(void)
{
	int iError = 0;

	switch (errno)
	{
		/* Base error codes */
		case EINTR:
			iError = WSAEINTR;
			break;

		case EBADF:
			iError = WSAEBADF;
			break;

		case EACCES:
			iError = WSAEACCES;
			break;

		case EFAULT:
			iError = WSAEFAULT;
			break;

		case EINVAL:
			iError = WSAEINVAL;
			break;

		case EMFILE:
			iError = WSAEMFILE;
			break;

			/* BSD sockets error codes */

		case EWOULDBLOCK:
			iError = WSAEWOULDBLOCK;
			break;

		case EINPROGRESS:
			iError = WSAEINPROGRESS;
			break;

		case EALREADY:
			iError = WSAEALREADY;
			break;

		case ENOTSOCK:
			iError = WSAENOTSOCK;
			break;

		case EDESTADDRREQ:
			iError = WSAEDESTADDRREQ;
			break;

		case EMSGSIZE:
			iError = WSAEMSGSIZE;
			break;

		case EPROTOTYPE:
			iError = WSAEPROTOTYPE;
			break;

		case ENOPROTOOPT:
			iError = WSAENOPROTOOPT;
			break;

		case EPROTONOSUPPORT:
			iError = WSAEPROTONOSUPPORT;
			break;

		case ESOCKTNOSUPPORT:
			iError = WSAESOCKTNOSUPPORT;
			break;

		case EOPNOTSUPP:
			iError = WSAEOPNOTSUPP;
			break;

		case EPFNOSUPPORT:
			iError = WSAEPFNOSUPPORT;
			break;

		case EAFNOSUPPORT:
			iError = WSAEAFNOSUPPORT;
			break;

		case EADDRINUSE:
			iError = WSAEADDRINUSE;
			break;

		case EADDRNOTAVAIL:
			iError = WSAEADDRNOTAVAIL;
			break;

		case ENETDOWN:
			iError = WSAENETDOWN;
			break;

		case ENETUNREACH:
			iError = WSAENETUNREACH;
			break;

		case ENETRESET:
			iError = WSAENETRESET;
			break;

		case ECONNABORTED:
			iError = WSAECONNABORTED;
			break;

		case ECONNRESET:
			iError = WSAECONNRESET;
			break;

		case ENOBUFS:
			iError = WSAENOBUFS;
			break;

		case EISCONN:
			iError = WSAEISCONN;
			break;

		case ENOTCONN:
			iError = WSAENOTCONN;
			break;

		case ESHUTDOWN:
			iError = WSAESHUTDOWN;
			break;

		case ETOOMANYREFS:
			iError = WSAETOOMANYREFS;
			break;

		case ETIMEDOUT:
			iError = WSAETIMEDOUT;
			break;

		case ECONNREFUSED:
			iError = WSAECONNREFUSED;
			break;

		case ELOOP:
			iError = WSAELOOP;
			break;

		case ENAMETOOLONG:
			iError = WSAENAMETOOLONG;
			break;

		case EHOSTDOWN:
			iError = WSAEHOSTDOWN;
			break;

		case EHOSTUNREACH:
			iError = WSAEHOSTUNREACH;
			break;

		case ENOTEMPTY:
			iError = WSAENOTEMPTY;
			break;
#ifdef EPROCLIM

		case EPROCLIM:
			iError = WSAEPROCLIM;
			break;
#endif

		case EUSERS:
			iError = WSAEUSERS;
			break;

		case EDQUOT:
			iError = WSAEDQUOT;
			break;

		case ESTALE:
			iError = WSAESTALE;
			break;

		case EREMOTE:
			iError = WSAEREMOTE;
			break;
			/* Special cases */
#if (EAGAIN != EWOULDBLOCK)

		case EAGAIN:
			iError = WSAEWOULDBLOCK;
			break;
#endif
#if defined(EPROTO)

		case EPROTO:
			iError = WSAECONNRESET;
			break;
#endif
		default:
			break;
	}

	/**
	 * Windows Sockets Extended Error Codes:
	 *
	 * WSASYSNOTREADY
	 * WSAVERNOTSUPPORTED
	 * WSANOTINITIALISED
	 * WSAEDISCON
	 * WSAENOMORE
	 * WSAECANCELLED
	 * WSAEINVALIDPROCTABLE
	 * WSAEINVALIDPROVIDER
	 * WSAEPROVIDERFAILEDINIT
	 * WSASYSCALLFAILURE
	 * WSASERVICE_NOT_FOUND
	 * WSATYPE_NOT_FOUND
	 * WSA_E_NO_MORE
	 * WSA_E_CANCELLED
	 * WSAEREFUSED
	 */
	return iError;
}

HANDLE WSACreateEvent(void)
{
	return CreateEvent(nullptr, TRUE, FALSE, nullptr);
}

BOOL WSASetEvent(HANDLE hEvent)
{
	return SetEvent(hEvent);
}

BOOL WSAResetEvent(WINPR_ATTR_UNUSED HANDLE hEvent)
{
	/* POSIX systems auto reset the socket,
	 * if no more data is available. */
	return TRUE;
}

BOOL WSACloseEvent(HANDLE hEvent)
{
	BOOL status = CloseHandle(hEvent);

	if (!status)
		SetLastError(6);

	return status;
}

int WSAEventSelect(SOCKET s, WSAEVENT hEventObject, LONG lNetworkEvents)
{
	u_long arg = 1;
	ULONG mode = 0;

	if (_ioctlsocket(s, FIONBIO, &arg) != 0)
		return SOCKET_ERROR;

	if (arg == 0)
		return 0;

	if (lNetworkEvents & FD_READ)
		mode |= WINPR_FD_READ;

	if (lNetworkEvents & FD_WRITE)
		mode |= WINPR_FD_WRITE;

	if (SetEventFileDescriptor(hEventObject, (int)s, mode) < 0)
		return SOCKET_ERROR;

	return 0;
}

DWORD WSAWaitForMultipleEvents(DWORD cEvents, const HANDLE* lphEvents, BOOL fWaitAll,
                               DWORD dwTimeout, BOOL fAlertable)
{
	return WaitForMultipleObjectsEx(cEvents, lphEvents, fWaitAll, dwTimeout, fAlertable);
}

SOCKET WSASocketA(int af, int type, int protocol,
                  WINPR_ATTR_UNUSED LPWSAPROTOCOL_INFOA lpProtocolInfo, WINPR_ATTR_UNUSED GROUP g,
                  WINPR_ATTR_UNUSED DWORD dwFlags)
{
	SOCKET s = 0;
	s = _socket(af, type, protocol);
	return s;
}

SOCKET WSASocketW(int af, int type, int protocol, LPWSAPROTOCOL_INFOW lpProtocolInfo, GROUP g,
                  DWORD dwFlags)
{
	return WSASocketA(af, type, protocol, (LPWSAPROTOCOL_INFOA)lpProtocolInfo, g, dwFlags);
}

int WSAIoctl(SOCKET s, DWORD dwIoControlCode, WINPR_ATTR_UNUSED LPVOID lpvInBuffer,
             WINPR_ATTR_UNUSED DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
             LPDWORD lpcbBytesReturned, WINPR_ATTR_UNUSED LPWSAOVERLAPPED lpOverlapped,
             WINPR_ATTR_UNUSED LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
	int fd = 0;
	int index = 0;
	ULONG nFlags = 0;
	size_t offset = 0;
	size_t ifreq_len = 0;
	struct ifreq* ifreq = nullptr;
	struct ifconf ifconf = WINPR_C_ARRAY_INIT;
	char address[128] = WINPR_C_ARRAY_INIT;
	char broadcast[128] = WINPR_C_ARRAY_INIT;
	char netmask[128] = WINPR_C_ARRAY_INIT;
	char buffer[4096] = WINPR_C_ARRAY_INIT;
	size_t numInterfaces = 0;
	size_t maxNumInterfaces = 0;
	INTERFACE_INFO* pInterface = nullptr;
	INTERFACE_INFO* pInterfaces = nullptr;
	struct sockaddr_in* pAddress = nullptr;
	struct sockaddr_in* pBroadcast = nullptr;
	struct sockaddr_in* pNetmask = nullptr;

	if ((dwIoControlCode != SIO_GET_INTERFACE_LIST) ||
	    (!lpvOutBuffer || !cbOutBuffer || !lpcbBytesReturned))
	{
		WSASetLastError(WSAEINVAL);
		return SOCKET_ERROR;
	}

	fd = (int)s;
	pInterfaces = (INTERFACE_INFO*)lpvOutBuffer;
	maxNumInterfaces = cbOutBuffer / sizeof(INTERFACE_INFO);
#ifdef WSAIOCTL_IFADDRS
	{
		struct ifaddrs* ifap = nullptr;

		if (getifaddrs(&ifap) != 0)
		{
			WSASetLastError(WSAENETDOWN);
			return SOCKET_ERROR;
		}

		index = 0;
		numInterfaces = 0;

		for (struct ifaddrs* ifa = ifap; ifa; ifa = ifa->ifa_next)
		{
			pInterface = &pInterfaces[index];
			pAddress = (struct sockaddr_in*)&pInterface->iiAddress;
			pBroadcast = (struct sockaddr_in*)&pInterface->iiBroadcastAddress;
			pNetmask = (struct sockaddr_in*)&pInterface->iiNetmask;
			nFlags = 0;

			if (ifa->ifa_flags & IFF_UP)
				nFlags |= _IFF_UP;

			if (ifa->ifa_flags & IFF_BROADCAST)
				nFlags |= _IFF_BROADCAST;

			if (ifa->ifa_flags & IFF_LOOPBACK)
				nFlags |= _IFF_LOOPBACK;

			if (ifa->ifa_flags & IFF_POINTOPOINT)
				nFlags |= _IFF_POINTTOPOINT;

			if (ifa->ifa_flags & IFF_MULTICAST)
				nFlags |= _IFF_MULTICAST;

			pInterface->iiFlags = nFlags;

			if (ifa->ifa_addr)
			{
				if ((ifa->ifa_addr->sa_family != AF_INET) && (ifa->ifa_addr->sa_family != AF_INET6))
					continue;

				getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr), address, sizeof(address), 0, 0,
				            NI_NUMERICHOST);
				inet_pton(ifa->ifa_addr->sa_family, address, (void*)&pAddress->sin_addr);
			}
			else
			{
				ZeroMemory(pAddress, sizeof(struct sockaddr_in));
			}

			if (ifa->ifa_dstaddr)
			{
				if ((ifa->ifa_dstaddr->sa_family != AF_INET) &&
				    (ifa->ifa_dstaddr->sa_family != AF_INET6))
					continue;

				getnameinfo(ifa->ifa_dstaddr, sizeof(struct sockaddr), broadcast, sizeof(broadcast),
				            0, 0, NI_NUMERICHOST);
				inet_pton(ifa->ifa_dstaddr->sa_family, broadcast, (void*)&pBroadcast->sin_addr);
			}
			else
			{
				ZeroMemory(pBroadcast, sizeof(struct sockaddr_in));
			}

			if (ifa->ifa_netmask)
			{
				if ((ifa->ifa_netmask->sa_family != AF_INET) &&
				    (ifa->ifa_netmask->sa_family != AF_INET6))
					continue;

				getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr), netmask, sizeof(netmask), 0,
				            0, NI_NUMERICHOST);
				inet_pton(ifa->ifa_netmask->sa_family, netmask, (void*)&pNetmask->sin_addr);
			}
			else
			{
				ZeroMemory(pNetmask, sizeof(struct sockaddr_in));
			}

			numInterfaces++;
			index++;
		}

		*lpcbBytesReturned = (DWORD)(numInterfaces * sizeof(INTERFACE_INFO));
		freeifaddrs(ifap);
		return 0;
	}
#endif
	ifconf.ifc_len = sizeof(buffer);
	ifconf.ifc_buf = buffer;

	if (ioctl(fd, SIOCGIFCONF, &ifconf) != 0)
	{
		WSASetLastError(WSAENETDOWN);
		return SOCKET_ERROR;
	}

	index = 0;
	offset = 0;
	numInterfaces = 0;
	ifreq = ifconf.ifc_req;

	while ((ifconf.ifc_len >= 0) && (offset < (size_t)ifconf.ifc_len) &&
	       (numInterfaces < maxNumInterfaces))
	{
		pInterface = &pInterfaces[index];
		pAddress = (struct sockaddr_in*)&pInterface->iiAddress;
		pBroadcast = (struct sockaddr_in*)&pInterface->iiBroadcastAddress;
		pNetmask = (struct sockaddr_in*)&pInterface->iiNetmask;

		if (ioctl(fd, SIOCGIFFLAGS, ifreq) != 0)
			goto next_ifreq;

		nFlags = 0;

		if (ifreq->ifr_flags & IFF_UP)
			nFlags |= _IFF_UP;

		if (ifreq->ifr_flags & IFF_BROADCAST)
			nFlags |= _IFF_BROADCAST;

		if (ifreq->ifr_flags & IFF_LOOPBACK)
			nFlags |= _IFF_LOOPBACK;

		if (ifreq->ifr_flags & IFF_POINTOPOINT)
			nFlags |= _IFF_POINTTOPOINT;

		if (ifreq->ifr_flags & IFF_MULTICAST)
			nFlags |= _IFF_MULTICAST;

		pInterface->iiFlags = nFlags;

		if (ioctl(fd, SIOCGIFADDR, ifreq) != 0)
			goto next_ifreq;

		if ((ifreq->ifr_addr.sa_family != AF_INET) && (ifreq->ifr_addr.sa_family != AF_INET6))
			goto next_ifreq;

		getnameinfo(&ifreq->ifr_addr, sizeof(ifreq->ifr_addr), address, sizeof(address), nullptr, 0,
		            NI_NUMERICHOST);
		inet_pton(ifreq->ifr_addr.sa_family, address, (void*)&pAddress->sin_addr);

		if (ioctl(fd, SIOCGIFBRDADDR, ifreq) != 0)
			goto next_ifreq;

		if ((ifreq->ifr_addr.sa_family != AF_INET) && (ifreq->ifr_addr.sa_family != AF_INET6))
			goto next_ifreq;

		getnameinfo(&ifreq->ifr_addr, sizeof(ifreq->ifr_addr), broadcast, sizeof(broadcast),
		            nullptr, 0, NI_NUMERICHOST);
		inet_pton(ifreq->ifr_addr.sa_family, broadcast, (void*)&pBroadcast->sin_addr);

		if (ioctl(fd, SIOCGIFNETMASK, ifreq) != 0)
			goto next_ifreq;

		if ((ifreq->ifr_addr.sa_family != AF_INET) && (ifreq->ifr_addr.sa_family != AF_INET6))
			goto next_ifreq;

		getnameinfo(&ifreq->ifr_addr, sizeof(ifreq->ifr_addr), netmask, sizeof(netmask), nullptr, 0,
		            NI_NUMERICHOST);
		inet_pton(ifreq->ifr_addr.sa_family, netmask, (void*)&pNetmask->sin_addr);
		numInterfaces++;
	next_ifreq:
#if !defined(__linux__) && !defined(__sun__) && !defined(__CYGWIN__) && !defined(EMSCRIPTEN)
		ifreq_len = IFNAMSIZ + ifreq->ifr_addr.sa_len;
#else
		ifreq_len = sizeof(*ifreq);
#endif
		ifreq = (struct ifreq*)&((BYTE*)ifreq)[ifreq_len];
		offset += ifreq_len;
		index++;
	}

	*lpcbBytesReturned = (DWORD)(numInterfaces * sizeof(INTERFACE_INFO));
	return 0;
}

SOCKET _accept(SOCKET s, struct sockaddr* addr, int* addrlen)
{
	int fd = WINPR_ASSERTING_INT_CAST(int, s);
	socklen_t s_addrlen = (socklen_t)*addrlen;
	const int status = accept(fd, addr, &s_addrlen);
	*addrlen = (int)s_addrlen;
	return (SOCKET)status;
}

int _bind(SOCKET s, const struct sockaddr* addr, int namelen)
{
	int status = 0;
	int fd = (int)s;
	status = bind(fd, addr, (socklen_t)namelen);

	if (status < 0)
		return SOCKET_ERROR;

	return status;
}

int closesocket(SOCKET s)
{
	int status = 0;
	int fd = (int)s;
	status = close(fd);
	return status;
}

int _connect(SOCKET s, const struct sockaddr* name, int namelen)
{
	int status = 0;
	int fd = (int)s;
	status = connect(fd, name, (socklen_t)namelen);

	if (status < 0)
		return SOCKET_ERROR;

	return status;
}

// NOLINTNEXTLINE(readability-non-const-parameter)
int _ioctlsocket(SOCKET s, long cmd, u_long* argp)
{
	int fd = (int)s;

	if (cmd == FIONBIO)
	{
		int flags = 0;

		if (!argp)
			return SOCKET_ERROR;

		flags = fcntl(fd, F_GETFL);

		if (flags == -1)
			return SOCKET_ERROR;

		if (*argp)
			(void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
		else
			(void)fcntl(fd, F_SETFL, flags & ~(O_NONBLOCK));
	}

	return 0;
}

int _getpeername(SOCKET s, struct sockaddr* name, int* namelen)
{
	int status = 0;
	int fd = (int)s;
	socklen_t s_namelen = (socklen_t)*namelen;
	status = getpeername(fd, name, &s_namelen);
	*namelen = (int)s_namelen;
	return status;
}

int _getsockname(SOCKET s, struct sockaddr* name, int* namelen)
{
	int status = 0;
	int fd = (int)s;
	socklen_t s_namelen = (socklen_t)*namelen;
	status = getsockname(fd, name, &s_namelen);
	*namelen = (int)s_namelen;
	return status;
}

int _getsockopt(SOCKET s, int level, int optname, char* optval, int* optlen)
{
	int status = 0;
	int fd = (int)s;
	socklen_t s_optlen = (socklen_t)*optlen;
	status = getsockopt(fd, level, optname, (void*)optval, &s_optlen);
	*optlen = (int)s_optlen;
	return status;
}

u_long _htonl(u_long hostlong)
{
	WINPR_ASSERT(hostlong <= UINT32_MAX);
	return htonl((UINT32)hostlong);
}

u_short _htons(u_short hostshort)
{
	return htons(hostshort);
}

unsigned long _inet_addr(const char* cp)
{
	return WINPR_ASSERTING_INT_CAST(unsigned long, inet_addr(cp));
}

char* _inet_ntoa(struct in_addr in)
{
	// NOLINTNEXTLINE(concurrency-mt-unsafe)
	return inet_ntoa(in);
}

int _listen(SOCKET s, int backlog)
{
	int status = 0;
	int fd = (int)s;
	status = listen(fd, backlog);
	return status;
}

u_long _ntohl(u_long netlong)
{
	WINPR_ASSERT((netlong & 0xFFFFFFFF00000000ULL) == 0);
	return ntohl((UINT32)netlong);
}

u_short _ntohs(u_short netshort)
{
	return ntohs(netshort);
}

int _recv(SOCKET s, char* buf, int len, int flags)
{
	int status = 0;
	int fd = (int)s;
	status = (int)recv(fd, (void*)buf, (size_t)len, flags);
	return status;
}

int _recvfrom(SOCKET s, char* buf, int len, int flags, struct sockaddr* from, int* fromlen)
{
	int status = 0;
	int fd = (int)s;
	socklen_t s_fromlen = (socklen_t)*fromlen;
	status = (int)recvfrom(fd, (void*)buf, (size_t)len, flags, from, &s_fromlen);
	*fromlen = (int)s_fromlen;
	return status;
}

int _select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
            const struct timeval* timeout)
{
	int status = 0;
	union
	{
		const struct timeval* cpv;
		struct timeval* pv;
	} cnv;
	cnv.cpv = timeout;
	do
	{
		status = select(nfds, readfds, writefds, exceptfds, cnv.pv);
	} while ((status < 0) && (errno == EINTR));

	return status;
}

int _send(SOCKET s, const char* buf, int len, int flags)
{
	int status = 0;
	int fd = (int)s;
	flags |= MSG_NOSIGNAL;
	status = (int)send(fd, (const void*)buf, (size_t)len, flags);
	return status;
}

int _sendto(SOCKET s, const char* buf, int len, int flags, const struct sockaddr* to, int tolen)
{
	int status = 0;
	int fd = (int)s;
	status = (int)sendto(fd, (const void*)buf, (size_t)len, flags, to, (socklen_t)tolen);
	return status;
}

int _setsockopt(SOCKET s, int level, int optname, const char* optval, int optlen)
{
	int status = 0;
	int fd = (int)s;
	status = setsockopt(fd, level, optname, (const void*)optval, (socklen_t)optlen);
	return status;
}

int _shutdown(SOCKET s, int how)
{
	int status = 0;
	int fd = (int)s;
	int s_how = -1;

	switch (how)
	{
		case SD_RECEIVE:
			s_how = SHUT_RD;
			break;

		case SD_SEND:
			s_how = SHUT_WR;
			break;

		case SD_BOTH:
			s_how = SHUT_RDWR;
			break;
		default:
			break;
	}

	if (s_how < 0)
		return SOCKET_ERROR;

	status = shutdown(fd, s_how);
	return status;
}

SOCKET _socket(int af, int type, int protocol)
{
	int fd = 0;
	SOCKET s = 0;
	fd = socket(af, type, protocol);

	if (fd < 0)
		return INVALID_SOCKET;

	s = (SOCKET)fd;
	return s;
}

struct hostent* _gethostbyaddr(const char* addr, int len, int type)
{
	struct hostent* host = nullptr;
	// NOLINTNEXTLINE(concurrency-mt-unsafe)
	host = gethostbyaddr((const void*)addr, (socklen_t)len, type);
	return host;
}

struct hostent* _gethostbyname(const char* name)
{
	// NOLINTNEXTLINE(concurrency-mt-unsafe)
	struct hostent* host = gethostbyname(name);
	return host;
}

int _gethostname(char* name, int namelen)
{
	int status = 0;
	status = gethostname(name, (size_t)namelen);
	return status;
}

struct servent* /* codespell:ignore servent */ _getservbyport(int port, const char* proto)
{
	// NOLINTNEXTLINE(concurrency-mt-unsafe)
	return getservbyport(port, proto);
}

struct servent*                                     /* codespell:ignore servent */
_getservbyname(const char* name, const char* proto) // codespell:ignore servent

{
	// NOLINTNEXTLINE(concurrency-mt-unsafe)
	return getservbyname(name, proto);
}

struct protoent* _getprotobynumber(int number)
{
	// NOLINTNEXTLINE(concurrency-mt-unsafe)
	return getprotobynumber(number);
}

struct protoent* _getprotobyname(const char* name)
{
	// NOLINTNEXTLINE(concurrency-mt-unsafe)
	return getprotobyname(name);
}

#endif /* _WIN32 */
