400 lines
10 KiB
C
Raw Normal View History

/* Copyright (C) 2010-2022 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (net_ifinfo.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
2022-06-25 11:37:12 -03:00
#include <stdio.h>
2022-06-25 11:37:12 -03:00
#include <string/stdstring.h>
#include <net/net_compat.h>
#if defined(_WIN32) && !defined(_XBOX)
#ifdef _MSC_VER
#pragma comment(lib, "Iphlpapi")
#endif
#include <iphlpapi.h>
2022-06-25 11:37:12 -03:00
#elif !defined(GEKKO)
#if defined(WANT_IFADDRS)
#include <compat/ifaddrs.h>
2022-06-25 11:37:12 -03:00
#elif !defined(HAVE_LIBNX) && !defined(_3DS)
#include <ifaddrs.h>
2022-06-25 14:19:26 -03:00
#ifndef WIIU
2022-06-25 11:37:12 -03:00
#include <net/if.h>
#endif
2018-10-12 17:29:24 +02:00
#endif
2022-06-25 14:19:26 -03:00
#endif
#include <net/net_ifinfo.h>
2022-06-25 11:37:12 -03:00
bool net_ifinfo_new(net_ifinfo_t *list)
{
2022-06-25 11:37:12 -03:00
#if defined(_WIN32) && !defined(_XBOX)
/* Microsoft docs recommend doing it this way. */
char buf[512];
ULONG result;
PIP_ADAPTER_ADDRESSES addr;
struct net_ifinfo_entry *entry;
size_t interfaces = 0;
ULONG flags = GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
ULONG len = 15 * 1024;
PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES)calloc(1, len);
list->entries = NULL;
if (!addresses)
goto failure;
result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addresses, &len);
if (result == ERROR_BUFFER_OVERFLOW)
{
2022-06-25 11:37:12 -03:00
PIP_ADAPTER_ADDRESSES new_addresses =
(PIP_ADAPTER_ADDRESSES)realloc(addresses, len);
2022-06-25 11:37:12 -03:00
if (new_addresses)
{
memset(new_addresses, 0, len);
2022-06-25 11:37:12 -03:00
addresses = new_addresses;
result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
addresses, &len);
}
}
2022-06-25 11:37:12 -03:00
if (result != ERROR_SUCCESS)
goto failure;
2022-06-25 11:37:12 -03:00
/* Count the number of valid interfaces first. */
addr = addresses;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
do
{
PIP_ADAPTER_UNICAST_ADDRESS unicast_addr = addr->FirstUnicastAddress;
if (!unicast_addr)
continue;
if (addr->OperStatus != IfOperStatusUp)
continue;
2022-06-25 11:37:12 -03:00
do
{
interfaces++;
} while ((unicast_addr = unicast_addr->Next));
} while ((addr = addr->Next));
2022-06-25 11:37:12 -03:00
if (!interfaces)
goto failure;
2022-06-25 11:37:12 -03:00
list->entries =
(struct net_ifinfo_entry*)calloc(interfaces, sizeof(*list->entries));
if (!list->entries)
2022-06-25 11:37:12 -03:00
goto failure;
list->size = 0;
2022-06-25 11:37:12 -03:00
/* Now create the entries. */
addr = addresses;
entry = list->entries;
2022-06-25 11:37:12 -03:00
do
{
PIP_ADAPTER_UNICAST_ADDRESS unicast_addr = addr->FirstUnicastAddress;
2022-06-25 11:37:12 -03:00
if (!unicast_addr)
continue;
if (addr->OperStatus != IfOperStatusUp)
continue;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
buf[0] = '\0';
if (addr->FriendlyName)
{
if (!WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, -1,
buf, sizeof(buf), NULL, NULL))
buf[0] = '\0'; /* Empty name on conversion failure. */
}
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
do
{
if (getnameinfo(unicast_addr->Address.lpSockaddr,
unicast_addr->Address.iSockaddrLength,
entry->host, sizeof(entry->host), NULL, 0, NI_NUMERICHOST))
continue;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
strlcpy(entry->name, buf, sizeof(entry->name));
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
if (++list->size >= interfaces)
break;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
entry++;
} while ((unicast_addr = unicast_addr->Next));
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
if (list->size >= interfaces)
break;
} while ((addr = addr->Next));
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
free(addresses);
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
return true;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
failure:
free(addresses);
net_ifinfo_free(list);
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
return false;
#elif defined(HAVE_LIBNX) || defined(_3DS) || defined(GEKKO)
uint32_t addr = 0;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
list->entries = (struct net_ifinfo_entry*)calloc(2, sizeof(*list->entries));
if (!list->entries)
{
list->size = 0;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
return false;
}
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
strcpy_literal(list->entries[0].name, "lo");
strcpy_literal(list->entries[0].host, "127.0.0.1");
list->size = 1;
2018-10-12 17:29:24 +02:00
2022-06-25 11:37:12 -03:00
#if defined(HAVE_LIBNX)
{
2022-06-25 11:37:12 -03:00
Result rc = nifmGetCurrentIpAddress(&addr);
2022-06-25 11:37:12 -03:00
if (!R_SUCCEEDED(rc))
return true;
}
#elif defined(_3DS)
addr = gethostid();
#else
addr = net_gethostip();
#endif
if (addr)
{
uint8_t *addr8 = (uint8_t*)&addr;
2022-06-25 11:37:12 -03:00
strcpy_literal(list->entries[1].name,
#if defined(HAVE_LIBNX)
"switch"
#elif defined(_3DS)
"wlan"
#else
"gekko"
#endif
);
snprintf(list->entries[1].host, sizeof(list->entries[1].host),
"%d.%d.%d.%d",
(int)addr8[0], (int)addr8[1], (int)addr8[2], (int)addr8[3]);
list->size++;
}
2022-06-25 11:37:12 -03:00
return true;
#else
struct ifaddrs *addr;
struct net_ifinfo_entry *entry;
size_t interfaces = 0;
struct ifaddrs *addresses = NULL;
2022-06-25 11:37:12 -03:00
list->entries = NULL;
2022-06-25 11:37:12 -03:00
if (getifaddrs(&addresses) || !addresses)
goto failure;
2022-06-25 11:37:12 -03:00
/* Count the number of valid interfaces first. */
addr = addresses;
2022-06-25 11:37:12 -03:00
do
{
if (!addr->ifa_addr)
continue;
2022-06-25 14:19:26 -03:00
#ifndef WIIU
2022-06-25 11:37:12 -03:00
if (!(addr->ifa_flags & IFF_UP))
continue;
2022-06-25 14:19:26 -03:00
#endif
2022-06-25 11:37:12 -03:00
switch (addr->ifa_addr->sa_family)
{
case AF_INET:
case AF_INET6:
interfaces++;
break;
default:
break;
}
2022-06-25 11:37:12 -03:00
} while ((addr = addr->ifa_next));
2022-06-25 11:37:12 -03:00
if (!interfaces)
goto failure;
2022-06-25 11:37:12 -03:00
list->entries =
(struct net_ifinfo_entry*)calloc(interfaces, sizeof(*list->entries));
if (!list->entries)
goto failure;
list->size = 0;
2022-06-25 11:37:12 -03:00
/* Now create the entries. */
addr = addresses;
entry = list->entries;
2022-06-25 11:37:12 -03:00
do
{
2022-06-25 11:37:12 -03:00
socklen_t addrlen;
2022-06-25 11:37:12 -03:00
if (!addr->ifa_addr)
continue;
2022-06-25 14:19:26 -03:00
#ifndef WIIU
2022-06-25 11:37:12 -03:00
if (!(addr->ifa_flags & IFF_UP))
continue;
2022-06-25 14:19:26 -03:00
#endif
2022-06-25 11:37:12 -03:00
switch (addr->ifa_addr->sa_family)
{
case AF_INET:
addrlen = sizeof(struct sockaddr_in);
break;
case AF_INET6:
addrlen = sizeof(struct sockaddr_in6);
break;
default:
continue;
}
2022-06-25 11:37:12 -03:00
if (getnameinfo(addr->ifa_addr, addrlen,
entry->host, sizeof(entry->host), NULL, 0, NI_NUMERICHOST))
continue;
2022-06-25 11:37:12 -03:00
if (addr->ifa_name)
strlcpy(entry->name, addr->ifa_name, sizeof(entry->name));
2022-06-25 11:37:12 -03:00
if (++list->size >= interfaces)
break;
2022-06-25 11:37:12 -03:00
entry++;
} while ((addr = addr->ifa_next));
2022-06-25 11:37:12 -03:00
freeifaddrs(addresses);
return true;
2022-06-25 11:37:12 -03:00
failure:
freeifaddrs(addresses);
net_ifinfo_free(list);
return false;
2022-06-25 11:37:12 -03:00
#endif
}
void net_ifinfo_free(net_ifinfo_t *list)
{
free(list->entries);
list->entries = NULL;
list->size = 0;
}
bool net_ifinfo_best(const char *dst, void *src, bool ipv6)
{
bool ret = false;
/* TODO/FIXME: Implement for other platforms, if necessary. */
#if defined(_WIN32) && !defined(_XBOX)
if (!ipv6)
{
/* Courtesy of MiniUPnP: https://github.com/miniupnp/miniupnp */
DWORD index;
#ifdef __WINRT__
2022-06-25 11:37:12 -03:00
struct sockaddr_in dst_addr = {0};
#endif
2022-06-25 11:37:12 -03:00
IPAddr dst_ip = inet_addr(dst);
if (!src)
2022-06-25 11:37:12 -03:00
return false;
if (dst_ip == INADDR_NONE || dst_ip == INADDR_ANY)
return false;
#ifdef __WINRT__
2022-06-25 11:37:12 -03:00
dst_addr.sin_family = AF_INET;
dst_addr.sin_addr.s_addr = dst_ip;
if (GetBestInterfaceEx((struct sockaddr*)&dst_addr, &index) == NO_ERROR)
#else
2022-06-25 11:37:12 -03:00
if (GetBestInterface(dst_ip, &index) == NO_ERROR)
#endif
{
/* Microsoft docs recommend doing it this way. */
2022-06-25 11:37:12 -03:00
ULONG len = 15 * 1024;
PIP_ADAPTER_ADDRESSES addresses =
(PIP_ADAPTER_ADDRESSES)calloc(1, len);
if (addresses)
{
2022-06-25 11:37:12 -03:00
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
ULONG result = GetAdaptersAddresses(AF_INET, flags, NULL,
addresses, &len);
if (result == ERROR_BUFFER_OVERFLOW)
{
2022-06-25 11:37:12 -03:00
PIP_ADAPTER_ADDRESSES new_addresses =
(PIP_ADAPTER_ADDRESSES)realloc(addresses, len);
if (new_addresses)
{
memset(new_addresses, 0, len);
addresses = new_addresses;
result = GetAdaptersAddresses(AF_INET, flags, NULL,
addresses, &len);
}
}
if (result == NO_ERROR)
{
PIP_ADAPTER_ADDRESSES addr = addresses;
do
{
if (addr->IfIndex == index)
{
if (addr->FirstUnicastAddress)
{
struct sockaddr_in *addr_unicast =
2022-06-25 11:37:12 -03:00
(struct sockaddr_in*)
addr->FirstUnicastAddress->Address.lpSockaddr;
memcpy(src, &addr_unicast->sin_addr,
sizeof(addr_unicast->sin_addr));
ret = true;
}
break;
}
} while ((addr = addr->Next));
}
free(addresses);
}
}
}
#endif
return ret;
}