(Network) net_ifinfo refactor (#14102)

This commit is contained in:
Cthulhu-throwaway 2022-06-25 11:37:12 -03:00 committed by GitHub
parent a7dc57f68f
commit 14e5cd317b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 260 additions and 252 deletions

View File

@ -20,10 +20,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_NET_IFINFO_H
#define _LIBRETRO_NET_IFINFO_H
#ifndef _LIBRETRO_SDK_NET_IFINFO_H
#define _LIBRETRO_SDK_NET_IFINFO_H
#include <stdint.h>
#include <stddef.h>
#include <boolean.h>
@ -34,22 +33,20 @@ RETRO_BEGIN_DECLS
struct net_ifinfo_entry
{
char *name;
char *host;
char name[256];
char host[256];
};
struct net_ifinfo
typedef struct net_ifinfo
{
struct net_ifinfo_entry *entries;
size_t size;
};
typedef struct net_ifinfo net_ifinfo_t;
void net_ifinfo_free(net_ifinfo_t *list);
} net_ifinfo_t;
bool net_ifinfo_new(net_ifinfo_t *list);
void net_ifinfo_free(net_ifinfo_t *list);
bool net_ifinfo_best(const char *dst, void *src, bool ipv6);
RETRO_END_DECLS

View File

@ -20,274 +20,287 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <retro_miscellaneous.h>
#include <string/stdstring.h>
#include <net/net_compat.h>
#if defined(_WIN32) && !defined(_XBOX)
#ifdef _MSC_VER
#pragma comment(lib, "Iphlpapi")
#endif
#include <winsock2.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#elif defined (GEKKO) && !defined(WIIU)
#include <network.h>
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#ifdef WANT_IFADDRS
#elif !defined(GEKKO)
#if defined(WANT_IFADDRS)
#include <compat/ifaddrs.h>
#else
#if !defined HAVE_LIBNX && !defined(_3DS)
#elif !defined(HAVE_LIBNX) && !defined(_3DS)
#include <ifaddrs.h>
#endif
#include <net/if.h>
#endif
#endif
#include <net/net_ifinfo.h>
#if defined(BSD)
#include <netinet/in.h>
#endif
void net_ifinfo_free(net_ifinfo_t *list)
{
unsigned k;
if (!list)
return;
for (k = 0; k < list->size; k++)
{
struct net_ifinfo_entry *ptr =
(struct net_ifinfo_entry*)&list->entries[k];
if (*ptr->name)
free(ptr->name);
if (*ptr->host)
free(ptr->host);
ptr->name = NULL;
ptr->host = NULL;
}
free(list->entries);
}
#if defined(HAVE_LIBNX) || defined(_3DS) || defined(GEKKO)
static void convert_ip(char *dst, size_t size, uint32_t ip, bool inverted)
{
unsigned char bytes[4];
bytes[0] = ip & 0xFF;
bytes[1] = (ip >> 8) & 0xFF;
bytes[2] = (ip >> 16) & 0xFF;
bytes[3] = (ip >> 24) & 0xFF;
if (inverted)
snprintf(dst, size, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]);
else
snprintf(dst, size, "%d.%d.%d.%d", bytes[3], bytes[2], bytes[1], bytes[0]);
}
#endif
bool net_ifinfo_new(net_ifinfo_t *list)
{
#if defined(GEKKO)
char hostname[128];
#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);
memset(list, 0, sizeof(net_ifinfo_t));
list->entries = NULL;
/* loopback */
list->entries = (struct net_ifinfo_entry*)
malloc(2 * sizeof(struct net_ifinfo_entry));
if (!addresses)
goto failure;
if (!list->entries)
goto error;
list->entries[0].name = strdup("lo");
list->entries[0].host = strdup("127.0.0.1");
list->entries[1].name = strdup("gekko");
convert_ip(hostname, sizeof(hostname), net_gethostip(), false);
list->entries[1].host = strdup(hostname);
list->size = 2;
return true;
/*
actual interface
can be wlan or eth (with a wiiu adapter)
so we just use "switch" as a name
*/
#elif defined(HAVE_LIBNX) || defined(_3DS)
uint32_t id;
#ifdef HAVE_LIBNX
Result rc;
#endif
char hostname[128];
unsigned k = 0;
struct net_ifinfo_entry *ptr = NULL;
memset(list, 0, sizeof(net_ifinfo_t));
/* loopback */
convert_ip(hostname, sizeof(hostname), INADDR_LOOPBACK, false);
ptr = (struct net_ifinfo_entry*)
realloc(list->entries, (k+1) * sizeof(struct net_ifinfo_entry));
if (!ptr)
goto error;
list->entries = ptr;
list->entries[k].name = strdup("lo");
list->entries[k].host = strdup(hostname);
list->size = k + 1;
k++;
/*
actual interface
can be wlan or eth (with a wiiu adapter)
so we just use "switch" as a name
*/
#if defined(_3DS) || defined (GEKKO)
convert_ip(hostname, sizeof(hostname), gethostid(), true);
#else
rc = nifmGetCurrentIpAddress(&id);
if (!R_SUCCEEDED(rc)) /* not connected to any network */
return true;
convert_ip(hostname, sizeof(hostname), id, true);
#endif
ptr = (struct net_ifinfo_entry*)
realloc(list->entries, (k+1) * sizeof(struct net_ifinfo_entry));
if (!ptr)
goto error;
list->entries = ptr;
#if defined(_3DS)
list->entries[k].name = strdup("wlan");
#else
list->entries[k].name = strdup("switch");
#endif
list->entries[k].host = strdup(hostname);
list->size = k + 1;
return true;
#elif defined(_WIN32) && !defined(_XBOX)
unsigned k = 0;
PIP_ADAPTER_ADDRESSES adapter_addresses = NULL, aa = NULL;
PIP_ADAPTER_UNICAST_ADDRESS ua = NULL;
#ifdef _WIN32_WINNT_WINXP
DWORD size = 0;
DWORD rv = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &size);
adapter_addresses = (PIP_ADAPTER_ADDRESSES)malloc(size);
rv = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adapter_addresses, &size);
memset(list, 0, sizeof(net_ifinfo_t));
if (rv != ERROR_SUCCESS)
goto error;
#endif
for (aa = adapter_addresses; aa != NULL; aa = aa->Next)
result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addresses, &len);
if (result == ERROR_BUFFER_OVERFLOW)
{
char name[PATH_MAX_LENGTH];
memset(name, 0, sizeof(name));
PIP_ADAPTER_ADDRESSES new_addresses =
(PIP_ADAPTER_ADDRESSES)realloc(addresses, len);
WideCharToMultiByte(CP_UTF8, 0, aa->FriendlyName, wcslen(aa->FriendlyName),
name, PATH_MAX_LENGTH, NULL, NULL);
for (ua = aa->FirstUnicastAddress; ua != NULL; ua = ua->Next)
if (new_addresses)
{
char host[PATH_MAX_LENGTH];
struct net_ifinfo_entry *ptr = (struct net_ifinfo_entry*)
realloc(list->entries, (k+1) * sizeof(struct net_ifinfo_entry));
memset(new_addresses, 0, len);
if (!ptr)
goto error;
list->entries = ptr;
memset(host, 0, sizeof(host));
getnameinfo(ua->Address.lpSockaddr, ua->Address.iSockaddrLength,
host, sizeof(host), NULL, NI_MAXSERV, NI_NUMERICHOST);
list->entries[k].name = strdup(name);
list->entries[k].host = strdup(host);
list->size = k + 1;
k++;
addresses = new_addresses;
result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
addresses, &len);
}
}
if (result != ERROR_SUCCESS)
goto failure;
free(adapter_addresses);
#else
unsigned k = 0;
struct ifaddrs *ifa = NULL;
struct ifaddrs *ifaddr = NULL;
/* Count the number of valid interfaces first. */
addr = addresses;
memset(list, 0, sizeof(net_ifinfo_t));
if (getifaddrs(&ifaddr) == -1)
goto error;
if (!list)
goto error;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
do
{
char host[NI_MAXHOST];
struct net_ifinfo_entry *ptr = NULL;
PIP_ADAPTER_UNICAST_ADDRESS unicast_addr = addr->FirstUnicastAddress;
if (!ifa->ifa_addr)
if (!unicast_addr)
continue;
if (addr->OperStatus != IfOperStatusUp)
continue;
if (ifa->ifa_addr->sa_family != AF_INET)
do
{
interfaces++;
} while ((unicast_addr = unicast_addr->Next));
} while ((addr = addr->Next));
if (!interfaces)
goto failure;
list->entries =
(struct net_ifinfo_entry*)calloc(interfaces, sizeof(*list->entries));
if (!list->entries)
goto failure;
list->size = 0;
/* Now create the entries. */
addr = addresses;
entry = list->entries;
do
{
PIP_ADAPTER_UNICAST_ADDRESS unicast_addr = addr->FirstUnicastAddress;
if (!unicast_addr)
continue;
if (addr->OperStatus != IfOperStatusUp)
continue;
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in),
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0)
goto error;
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. */
}
ptr = (struct net_ifinfo_entry*)
realloc(list->entries, (k+1) * sizeof(struct net_ifinfo_entry));
do
{
if (getnameinfo(unicast_addr->Address.lpSockaddr,
unicast_addr->Address.iSockaddrLength,
entry->host, sizeof(entry->host), NULL, 0, NI_NUMERICHOST))
continue;
if (!ptr)
goto error;
strlcpy(entry->name, buf, sizeof(entry->name));
list->entries = ptr;
if (++list->size >= interfaces)
break;
list->entries[k].name = strdup(ifa->ifa_name);
list->entries[k].host = strdup(host);
list->size = k + 1;
entry++;
} while ((unicast_addr = unicast_addr->Next));
k++;
}
if (list->size >= interfaces)
break;
} while ((addr = addr->Next));
free(addresses);
freeifaddrs(ifaddr);
#endif
return true;
error:
#ifdef _WIN32
if (adapter_addresses)
free(adapter_addresses);
#elif !defined(HAVE_LIBNX) && !defined(_3DS) && !defined(GEKKO)
freeifaddrs(ifaddr);
#endif
failure:
free(addresses);
net_ifinfo_free(list);
return false;
#elif defined(HAVE_LIBNX) || defined(_3DS) || defined(GEKKO)
uint32_t addr = 0;
list->entries = (struct net_ifinfo_entry*)calloc(2, sizeof(*list->entries));
if (!list->entries)
{
list->size = 0;
return false;
}
strcpy_literal(list->entries[0].name, "lo");
strcpy_literal(list->entries[0].host, "127.0.0.1");
list->size = 1;
#if defined(HAVE_LIBNX)
{
Result rc = nifmGetCurrentIpAddress(&addr);
if (!R_SUCCEEDED(rc))
return true;
}
#elif defined(_3DS)
addr = gethostid();
#else
addr = net_gethostip();
#endif
if (addr)
{
uint8_t *addr8 = (uint8_t*)&addr;
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++;
}
return true;
#else
struct ifaddrs *addr;
struct net_ifinfo_entry *entry;
size_t interfaces = 0;
struct ifaddrs *addresses = NULL;
list->entries = NULL;
if (getifaddrs(&addresses) || !addresses)
goto failure;
/* Count the number of valid interfaces first. */
addr = addresses;
do
{
if (!addr->ifa_addr)
continue;
if (!(addr->ifa_flags & IFF_UP))
continue;
switch (addr->ifa_addr->sa_family)
{
case AF_INET:
case AF_INET6:
interfaces++;
break;
default:
break;
}
} while ((addr = addr->ifa_next));
if (!interfaces)
goto failure;
list->entries =
(struct net_ifinfo_entry*)calloc(interfaces, sizeof(*list->entries));
if (!list->entries)
goto failure;
list->size = 0;
/* Now create the entries. */
addr = addresses;
entry = list->entries;
do
{
socklen_t addrlen;
if (!addr->ifa_addr)
continue;
if (!(addr->ifa_flags & IFF_UP))
continue;
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;
}
if (getnameinfo(addr->ifa_addr, addrlen,
entry->host, sizeof(entry->host), NULL, 0, NI_NUMERICHOST))
continue;
if (addr->ifa_name)
strlcpy(entry->name, addr->ifa_name, sizeof(entry->name));
if (++list->size >= interfaces)
break;
entry++;
} while ((addr = addr->ifa_next));
freeifaddrs(addresses);
return true;
failure:
freeifaddrs(addresses);
net_ifinfo_free(list);
return false;
#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)
@ -300,42 +313,40 @@ bool net_ifinfo_best(const char *dst, void *src, bool ipv6)
{
/* Courtesy of MiniUPnP: https://github.com/miniupnp/miniupnp */
DWORD index;
unsigned long udst = inet_addr(dst);
#ifdef __WINRT__
struct sockaddr_in addr_dst = {0};
struct sockaddr_in dst_addr = {0};
#endif
if (udst == INADDR_NONE || udst == INADDR_ANY)
return ret;
IPAddr dst_ip = inet_addr(dst);
if (!src)
return ret;
return false;
if (dst_ip == INADDR_NONE || dst_ip == INADDR_ANY)
return false;
#ifdef __WINRT__
addr_dst.sin_family = AF_INET;
addr_dst.sin_addr.s_addr = udst;
if (GetBestInterfaceEx((struct sockaddr *) &addr_dst, &index)
== NO_ERROR)
dst_addr.sin_family = AF_INET;
dst_addr.sin_addr.s_addr = dst_ip;
if (GetBestInterfaceEx((struct sockaddr*)&dst_addr, &index) == NO_ERROR)
#else
if (GetBestInterface(udst, &index) == NO_ERROR)
if (GetBestInterface(dst_ip, &index) == NO_ERROR)
#endif
{
/* Microsoft docs recommend doing it this way. */
ULONG len = 15 * 1024;
PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES)calloc(1, len);
ULONG len = 15 * 1024;
PIP_ADAPTER_ADDRESSES addresses =
(PIP_ADAPTER_ADDRESSES)calloc(1, len);
if (addresses)
{
ULONG flags = GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER |
GAA_FLAG_SKIP_FRIENDLY_NAME;
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)
{
PIP_ADAPTER_ADDRESSES new_addresses = (PIP_ADAPTER_ADDRESSES)realloc(addresses, len);
PIP_ADAPTER_ADDRESSES new_addresses =
(PIP_ADAPTER_ADDRESSES)realloc(addresses, len);
if (new_addresses)
{
@ -358,8 +369,8 @@ bool net_ifinfo_best(const char *dst, void *src, bool ipv6)
if (addr->FirstUnicastAddress)
{
struct sockaddr_in *addr_unicast =
(struct sockaddr_in *)
addr->FirstUnicastAddress->Address.lpSockaddr;
(struct sockaddr_in*)
addr->FirstUnicastAddress->Address.lpSockaddr;
memcpy(src, &addr_unicast->sin_addr,
sizeof(addr_unicast->sin_addr));