/* 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 #include #include #include #if defined(_WIN32) && !defined(_XBOX) #ifdef _MSC_VER #pragma comment(lib, "Iphlpapi") #endif #include #elif !defined(VITA) && !defined(GEKKO) #if defined(WANT_IFADDRS) #include #elif !defined(HAVE_LIBNX) && !defined(_3DS) #include #ifndef WIIU #include #endif #endif #endif #include bool net_ifinfo_new(net_ifinfo_t *list) { #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) { 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_UNSPEC, flags, NULL, addresses, &len); } } if (result != ERROR_SUCCESS) goto failure; /* Count the number of valid interfaces first. */ addr = addresses; do { PIP_ADAPTER_UNICAST_ADDRESS unicast_addr = addr->FirstUnicastAddress; if (!unicast_addr) continue; if (addr->OperStatus != IfOperStatusUp) continue; do { interfaces++; } while ((unicast_addr = unicast_addr->Next)); } while ((addr = addr->Next)); if (!interfaces) goto failure; if (!(list->entries = (struct net_ifinfo_entry*)calloc(interfaces, sizeof(*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; 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. */ } do { if (getnameinfo_retro(unicast_addr->Address.lpSockaddr, unicast_addr->Address.iSockaddrLength, entry->host, sizeof(entry->host), NULL, 0, NI_NUMERICHOST)) continue; strlcpy(entry->name, buf, sizeof(entry->name)); if (++list->size >= interfaces) break; entry++; } while ((unicast_addr = unicast_addr->Next)); if (list->size >= interfaces) break; } while ((addr = addr->Next)); free(addresses); return true; failure: free(addresses); net_ifinfo_free(list); return false; #elif defined(VITA) SceNetCtlInfo info; if (!(list->entries = (struct net_ifinfo_entry*)calloc(2, sizeof(*list->entries)))) { list->size = 0; return false; } strlcpy(list->entries[0].name, "lo", sizeof(list->entries[0].name)); strlcpy(list->entries[0].host, "127.0.0.1", sizeof(list->entries[0].host)); list->size = 1; if (!sceNetCtlInetGetInfo(SCE_NETCTL_INFO_GET_IP_ADDRESS, &info)) { strlcpy(list->entries[1].name, "wlan", sizeof(list->entries[1].name)); strlcpy(list->entries[1].host, info.ip_address, sizeof(list->entries[1].host)); list->size++; } return true; #elif defined(HAVE_LIBNX) || defined(_3DS) || defined(GEKKO) uint32_t addr = 0; if (!(list->entries = (struct net_ifinfo_entry*)calloc(2, sizeof(*list->entries)))) { list->size = 0; return false; } strlcpy(list->entries[0].name, "lo", sizeof(list->entries[0].name)); strlcpy(list->entries[0].host, "127.0.0.1", sizeof(list->entries[0].host)); 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; strlcpy(list->entries[1].name, #if defined(HAVE_LIBNX) "switch" #elif defined(_3DS) "wlan" #else "gekko" #endif , sizeof(list->entries[1].name)); 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; #ifndef WIIU if (!(addr->ifa_flags & IFF_UP)) continue; #endif switch (addr->ifa_addr->sa_family) { case AF_INET: #ifdef HAVE_INET6 case AF_INET6: #endif 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; #ifndef WIIU if (!(addr->ifa_flags & IFF_UP)) continue; #endif switch (addr->ifa_addr->sa_family) { case AF_INET: addrlen = sizeof(struct sockaddr_in); break; #ifdef HAVE_INET6 case AF_INET6: addrlen = sizeof(struct sockaddr_in6); break; #endif default: continue; } if (getnameinfo_retro(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) { 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__ struct sockaddr_in dst_addr = {0}; #endif ULONG dst_ip = (ULONG)inet_addr(dst); if (!src) return false; if (dst_ip == INADDR_NONE || dst_ip == INADDR_ANY) return false; #ifdef __WINRT__ 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(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); if (addresses) { 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); 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 = (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; }