libretro: adjust psxclock description
[pcsx_rearmed.git] / deps / libretro-common / net / net_ifinfo.c
CommitLineData
3719602c
PC
1/* Copyright (C) 2010-2022 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (net_ifinfo.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#include <stdlib.h>
24#include <stdio.h>
25
26#include <string/stdstring.h>
27#include <net/net_compat.h>
28
29#if defined(_WIN32) && !defined(_XBOX)
30#ifdef _MSC_VER
31#pragma comment(lib, "Iphlpapi")
32#endif
33
34#include <iphlpapi.h>
35
36#elif !defined(VITA) && !defined(GEKKO)
37#if defined(WANT_IFADDRS)
38#include <compat/ifaddrs.h>
39#elif !defined(HAVE_LIBNX) && !defined(_3DS)
40#include <ifaddrs.h>
41#ifndef WIIU
42#include <net/if.h>
43#endif
44#endif
45#endif
46
47#include <net/net_ifinfo.h>
48
49bool net_ifinfo_new(net_ifinfo_t *list)
50{
51#if defined(_WIN32) && !defined(_XBOX)
52 /* Microsoft docs recommend doing it this way. */
53 char buf[512];
54 ULONG result;
55 PIP_ADAPTER_ADDRESSES addr;
56 struct net_ifinfo_entry *entry;
57 size_t interfaces = 0;
58 ULONG flags = GAA_FLAG_SKIP_ANYCAST
59 | GAA_FLAG_SKIP_MULTICAST
60 | GAA_FLAG_SKIP_DNS_SERVER;
61 ULONG len = 15 * 1024;
62 PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES)calloc(1, len);
63
64 list->entries = NULL;
65
66 if (!addresses)
67 goto failure;
68
69 result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addresses, &len);
70 if (result == ERROR_BUFFER_OVERFLOW)
71 {
72 PIP_ADAPTER_ADDRESSES new_addresses =
73 (PIP_ADAPTER_ADDRESSES)realloc(addresses, len);
74
75 if (new_addresses)
76 {
77 memset(new_addresses, 0, len);
78
79 addresses = new_addresses;
80 result = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
81 addresses, &len);
82 }
83 }
84 if (result != ERROR_SUCCESS)
85 goto failure;
86
87 /* Count the number of valid interfaces first. */
88 addr = addresses;
89
90 do
91 {
92 PIP_ADAPTER_UNICAST_ADDRESS unicast_addr = addr->FirstUnicastAddress;
93
94 if (!unicast_addr)
95 continue;
96 if (addr->OperStatus != IfOperStatusUp)
97 continue;
98
99 do
100 {
101 interfaces++;
102 } while ((unicast_addr = unicast_addr->Next));
103 } while ((addr = addr->Next));
104
105 if (!interfaces)
106 goto failure;
107
108 if (!(list->entries =
109 (struct net_ifinfo_entry*)calloc(interfaces, sizeof(*list->entries))))
110 goto failure;
111
112 list->size = 0;
113 /* Now create the entries. */
114 addr = addresses;
115 entry = list->entries;
116
117 do
118 {
119 PIP_ADAPTER_UNICAST_ADDRESS unicast_addr = addr->FirstUnicastAddress;
120
121 if (!unicast_addr)
122 continue;
123 if (addr->OperStatus != IfOperStatusUp)
124 continue;
125
126 buf[0] = '\0';
127 if (addr->FriendlyName)
128 {
129 if (!WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, -1,
130 buf, sizeof(buf), NULL, NULL))
131 buf[0] = '\0'; /* Empty name on conversion failure. */
132 }
133
134 do
135 {
136 if (getnameinfo_retro(unicast_addr->Address.lpSockaddr,
137 unicast_addr->Address.iSockaddrLength,
138 entry->host, sizeof(entry->host), NULL, 0, NI_NUMERICHOST))
139 continue;
140
141 strlcpy(entry->name, buf, sizeof(entry->name));
142
143 if (++list->size >= interfaces)
144 break;
145
146 entry++;
147 } while ((unicast_addr = unicast_addr->Next));
148
149 if (list->size >= interfaces)
150 break;
151 } while ((addr = addr->Next));
152
153 free(addresses);
154
155 return true;
156
157failure:
158 free(addresses);
159 net_ifinfo_free(list);
160
161 return false;
162#elif defined(VITA)
163 SceNetCtlInfo info;
164 if (!(list->entries = (struct net_ifinfo_entry*)calloc(2, sizeof(*list->entries))))
165 {
166 list->size = 0;
167 return false;
168 }
169
170 strlcpy(list->entries[0].name, "lo", sizeof(list->entries[0].name));
171 strlcpy(list->entries[0].host, "127.0.0.1", sizeof(list->entries[0].host));
172 list->size = 1;
173
174 if (!sceNetCtlInetGetInfo(SCE_NETCTL_INFO_GET_IP_ADDRESS, &info))
175 {
176 strlcpy(list->entries[1].name, "wlan", sizeof(list->entries[1].name));
177 strlcpy(list->entries[1].host, info.ip_address,
178 sizeof(list->entries[1].host));
179 list->size++;
180 }
181
182 return true;
183#elif defined(HAVE_LIBNX) || defined(_3DS) || defined(GEKKO)
184 uint32_t addr = 0;
185 if (!(list->entries = (struct net_ifinfo_entry*)calloc(2, sizeof(*list->entries))))
186 {
187 list->size = 0;
188 return false;
189 }
190
191 strlcpy(list->entries[0].name, "lo", sizeof(list->entries[0].name));
192 strlcpy(list->entries[0].host, "127.0.0.1", sizeof(list->entries[0].host));
193 list->size = 1;
194
195#if defined(HAVE_LIBNX)
196 {
197 Result rc = nifmGetCurrentIpAddress(&addr);
198
199 if (!R_SUCCEEDED(rc))
200 return true;
201 }
202#elif defined(_3DS)
203 addr = gethostid();
204#else
205 addr = net_gethostip();
206#endif
207 if (addr)
208 {
209 uint8_t *addr8 = (uint8_t*)&addr;
210 strlcpy(list->entries[1].name,
211#if defined(HAVE_LIBNX)
212 "switch"
213#elif defined(_3DS)
214 "wlan"
215#else
216 "gekko"
217#endif
218 , sizeof(list->entries[1].name));
219 snprintf(list->entries[1].host, sizeof(list->entries[1].host),
220 "%d.%d.%d.%d",
221 (int)addr8[0], (int)addr8[1], (int)addr8[2], (int)addr8[3]);
222 list->size++;
223 }
224
225 return true;
226#else
227 struct ifaddrs *addr;
228 struct net_ifinfo_entry *entry;
229 size_t interfaces = 0;
230 struct ifaddrs *addresses = NULL;
231
232 list->entries = NULL;
233
234 if (getifaddrs(&addresses) || !addresses)
235 goto failure;
236
237 /* Count the number of valid interfaces first. */
238 addr = addresses;
239
240 do
241 {
242 if (!addr->ifa_addr)
243 continue;
244#ifndef WIIU
245 if (!(addr->ifa_flags & IFF_UP))
246 continue;
247#endif
248
249 switch (addr->ifa_addr->sa_family)
250 {
251 case AF_INET:
252#ifdef HAVE_INET6
253 case AF_INET6:
254#endif
255 interfaces++;
256 break;
257 default:
258 break;
259 }
260 } while ((addr = addr->ifa_next));
261
262 if (!interfaces)
263 goto failure;
264
265 list->entries =
266 (struct net_ifinfo_entry*)calloc(interfaces, sizeof(*list->entries));
267 if (!list->entries)
268 goto failure;
269 list->size = 0;
270
271 /* Now create the entries. */
272 addr = addresses;
273 entry = list->entries;
274
275 do
276 {
277 socklen_t addrlen;
278
279 if (!addr->ifa_addr)
280 continue;
281#ifndef WIIU
282 if (!(addr->ifa_flags & IFF_UP))
283 continue;
284#endif
285
286 switch (addr->ifa_addr->sa_family)
287 {
288 case AF_INET:
289 addrlen = sizeof(struct sockaddr_in);
290 break;
291#ifdef HAVE_INET6
292 case AF_INET6:
293 addrlen = sizeof(struct sockaddr_in6);
294 break;
295#endif
296 default:
297 continue;
298 }
299
300 if (getnameinfo_retro(addr->ifa_addr, addrlen,
301 entry->host, sizeof(entry->host), NULL, 0, NI_NUMERICHOST))
302 continue;
303
304 if (addr->ifa_name)
305 strlcpy(entry->name, addr->ifa_name, sizeof(entry->name));
306
307 if (++list->size >= interfaces)
308 break;
309
310 entry++;
311 } while ((addr = addr->ifa_next));
312
313 freeifaddrs(addresses);
314
315 return true;
316
317failure:
318 freeifaddrs(addresses);
319 net_ifinfo_free(list);
320
321 return false;
322#endif
323}
324
325void net_ifinfo_free(net_ifinfo_t *list)
326{
327 free(list->entries);
328
329 list->entries = NULL;
330 list->size = 0;
331}
332
333bool net_ifinfo_best(const char *dst, void *src, bool ipv6)
334{
335 bool ret = false;
336
337/* TODO/FIXME: Implement for other platforms, if necessary. */
338#if defined(_WIN32) && !defined(_XBOX)
339 if (!ipv6)
340 {
341 /* Courtesy of MiniUPnP: https://github.com/miniupnp/miniupnp */
342 DWORD index;
343#ifdef __WINRT__
344 struct sockaddr_in dst_addr = {0};
345#endif
346 ULONG dst_ip = (ULONG)inet_addr(dst);
347
348 if (!src)
349 return false;
350 if (dst_ip == INADDR_NONE || dst_ip == INADDR_ANY)
351 return false;
352
353#ifdef __WINRT__
354 dst_addr.sin_family = AF_INET;
355 dst_addr.sin_addr.s_addr = dst_ip;
356 if (GetBestInterfaceEx((struct sockaddr*)&dst_addr, &index) == NO_ERROR)
357#else
358 if (GetBestInterface(dst_ip, &index) == NO_ERROR)
359#endif
360 {
361 /* Microsoft docs recommend doing it this way. */
362 ULONG len = 15 * 1024;
363 PIP_ADAPTER_ADDRESSES addresses =
364 (PIP_ADAPTER_ADDRESSES)calloc(1, len);
365
366 if (addresses)
367 {
368 ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
369 GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
370 ULONG result = GetAdaptersAddresses(AF_INET, flags, NULL,
371 addresses, &len);
372
373 if (result == ERROR_BUFFER_OVERFLOW)
374 {
375 PIP_ADAPTER_ADDRESSES new_addresses =
376 (PIP_ADAPTER_ADDRESSES)realloc(addresses, len);
377
378 if (new_addresses)
379 {
380 memset(new_addresses, 0, len);
381
382 addresses = new_addresses;
383 result = GetAdaptersAddresses(AF_INET, flags, NULL,
384 addresses, &len);
385 }
386 }
387
388 if (result == NO_ERROR)
389 {
390 PIP_ADAPTER_ADDRESSES addr = addresses;
391
392 do
393 {
394 if (addr->IfIndex == index)
395 {
396 if (addr->FirstUnicastAddress)
397 {
398 struct sockaddr_in *addr_unicast =
399 (struct sockaddr_in*)
400 addr->FirstUnicastAddress->Address.lpSockaddr;
401
402 memcpy(src, &addr_unicast->sin_addr,
403 sizeof(addr_unicast->sin_addr));
404
405 ret = true;
406 }
407
408 break;
409 }
410 } while ((addr = addr->Next));
411 }
412
413 free(addresses);
414 }
415 }
416 }
417#endif
418
419 return ret;
420}