libretro: adjust psxclock description
[pcsx_rearmed.git] / deps / libretro-common / net / net_ifinfo.c
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
49 bool 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
157 failure:
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
317 failure:
318    freeifaddrs(addresses);
319    net_ifinfo_free(list);
320
321    return false;
322 #endif
323 }
324
325 void net_ifinfo_free(net_ifinfo_t *list)
326 {
327    free(list->entries);
328
329    list->entries = NULL;
330    list->size    = 0;
331 }
332
333 bool 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 }