1 // Copyright 2017 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/IOS/Network/IP/Top.h"
6 
7 #include <algorithm>
8 #include <cstddef>
9 #include <cstring>
10 #include <memory>
11 #include <optional>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #ifndef _WIN32
17 #include <netdb.h>
18 #include <poll.h>
19 #endif
20 
21 #include <fmt/format.h>
22 
23 #include "Common/Assert.h"
24 #include "Common/CommonTypes.h"
25 #include "Common/Logging/Log.h"
26 #include "Common/Network.h"
27 #include "Common/ScopeGuard.h"
28 #include "Common/StringUtil.h"
29 
30 #include "Core/Core.h"
31 #include "Core/HW/Memmap.h"
32 #include "Core/IOS/Network/ICMP.h"
33 #include "Core/IOS/Network/MACUtils.h"
34 #include "Core/IOS/Network/Socket.h"
35 
36 #ifdef _WIN32
37 #include <iphlpapi.h>
38 #include <ws2tcpip.h>
39 
40 #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
41 #define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
42 
43 #else
44 #include <arpa/inet.h>
45 #include <ifaddrs.h>
46 #include <netinet/in.h>
47 #include <resolv.h>
48 #include <sys/socket.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #endif
52 
53 namespace IOS::HLE::Device
54 {
55 enum SOResultCode : s32
56 {
57   SO_ERROR_INVALID_REQUEST = -51,
58   SO_ERROR_HOST_NOT_FOUND = -305,
59 };
60 
NetIPTop(Kernel & ios,const std::string & device_name)61 NetIPTop::NetIPTop(Kernel& ios, const std::string& device_name) : Device(ios, device_name)
62 {
63 #ifdef _WIN32
64   int ret = WSAStartup(MAKEWORD(2, 2), &InitData);
65   INFO_LOG(IOS_NET, "WSAStartup: %d", ret);
66 #endif
67 }
68 
~NetIPTop()69 NetIPTop::~NetIPTop()
70 {
71 #ifdef _WIN32
72   WSACleanup();
73 #endif
74 }
75 
DoState(PointerWrap & p)76 void NetIPTop::DoState(PointerWrap& p)
77 {
78   DoStateShared(p);
79   WiiSockMan::GetInstance().DoState(p);
80 }
81 
inet_addr(u8 a,u8 b,u8 c,u8 d)82 static constexpr u32 inet_addr(u8 a, u8 b, u8 c, u8 d)
83 {
84   return (static_cast<u32>(a) << 24) | (static_cast<u32>(b) << 16) | (static_cast<u32>(c) << 8) | d;
85 }
86 
inet_pton(const char * src,unsigned char * dst)87 static int inet_pton(const char* src, unsigned char* dst)
88 {
89   int saw_digit = 0;
90   int octets = 0;
91   unsigned char tmp[4]{};
92   unsigned char* tp = tmp;
93   char ch;
94 
95   while ((ch = *src++) != '\0')
96   {
97     if (ch >= '0' && ch <= '9')
98     {
99       unsigned int newt = (*tp * 10) + (ch - '0');
100 
101       if (newt > 255)
102         return 0;
103       *tp = newt;
104       if (!saw_digit)
105       {
106         if (++octets > 4)
107           return 0;
108         saw_digit = 1;
109       }
110     }
111     else if (ch == '.' && saw_digit)
112     {
113       if (octets == 4)
114         return 0;
115       *++tp = 0;
116       saw_digit = 0;
117     }
118     else
119     {
120       return 0;
121     }
122   }
123   if (octets < 4)
124     return 0;
125   memcpy(dst, tmp, 4);
126   return 1;
127 }
128 
129 // Maps SOCKOPT level from Wii to native
MapWiiSockOptLevelToNative(u32 level)130 static s32 MapWiiSockOptLevelToNative(u32 level)
131 {
132   if (level == 0xFFFF)
133     return SOL_SOCKET;
134 
135   INFO_LOG(IOS_NET, "SO_SETSOCKOPT: unknown level %u", level);
136   return level;
137 }
138 
139 // Maps SOCKOPT optname from native to Wii
MapWiiSockOptNameToNative(u32 optname)140 static s32 MapWiiSockOptNameToNative(u32 optname)
141 {
142   switch (optname)
143   {
144   case 0x4:
145     return SO_REUSEADDR;
146   case 0x1001:
147     return SO_SNDBUF;
148   case 0x1002:
149     return SO_RCVBUF;
150   case 0x1009:
151     return SO_ERROR;
152   }
153 
154   INFO_LOG(IOS_NET, "SO_SETSOCKOPT: unknown optname %u", optname);
155   return optname;
156 }
157 
158 struct DefaultInterface
159 {
160   u32 inet;       ///< IPv4 address
161   u32 netmask;    ///< IPv4 subnet mask
162   u32 broadcast;  ///< IPv4 broadcast address
163 };
164 
GetSystemDefaultInterface()165 static std::optional<DefaultInterface> GetSystemDefaultInterface()
166 {
167 #ifdef _WIN32
168   std::unique_ptr<MIB_IPFORWARDTABLE> forward_table;
169   DWORD forward_table_size = 0;
170   if (GetIpForwardTable(nullptr, &forward_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
171   {
172     forward_table =
173         std::unique_ptr<MIB_IPFORWARDTABLE>((PMIB_IPFORWARDTABLE) operator new(forward_table_size));
174   }
175 
176   std::unique_ptr<MIB_IPADDRTABLE> ip_table;
177   DWORD ip_table_size = 0;
178   if (GetIpAddrTable(nullptr, &ip_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
179   {
180     ip_table = std::unique_ptr<MIB_IPADDRTABLE>((PMIB_IPADDRTABLE) operator new(ip_table_size));
181   }
182 
183   // find the interface IP used for the default route and use that
184   NET_IFINDEX ifIndex = NET_IFINDEX_UNSPECIFIED;
185   DWORD result = GetIpForwardTable(forward_table.get(), &forward_table_size, FALSE);
186   // can return ERROR_MORE_DATA on XP even after the first call
187   while (result == NO_ERROR || result == ERROR_MORE_DATA)
188   {
189     for (DWORD i = 0; i < forward_table->dwNumEntries; ++i)
190     {
191       if (forward_table->table[i].dwForwardDest == 0)
192       {
193         ifIndex = forward_table->table[i].dwForwardIfIndex;
194         break;
195       }
196     }
197 
198     if (result == NO_ERROR || ifIndex != NET_IFINDEX_UNSPECIFIED)
199       break;
200 
201     result = GetIpForwardTable(forward_table.get(), &forward_table_size, FALSE);
202   }
203 
204   if (ifIndex != NET_IFINDEX_UNSPECIFIED &&
205       GetIpAddrTable(ip_table.get(), &ip_table_size, FALSE) == NO_ERROR)
206   {
207     for (DWORD i = 0; i < ip_table->dwNumEntries; ++i)
208     {
209       const auto& entry = ip_table->table[i];
210       if (entry.dwIndex == ifIndex)
211         return DefaultInterface{entry.dwAddr, entry.dwMask, entry.dwBCastAddr};
212     }
213   }
214 #elif !defined(__ANDROID__)
215   // Assume that the address that is used to access the Internet corresponds
216   // to the default interface.
217   auto get_default_address = []() -> std::optional<in_addr> {
218     const int sock = socket(AF_INET, SOCK_DGRAM, 0);
219     Common::ScopeGuard sock_guard{[sock] { close(sock); }};
220 
221     sockaddr_in addr{};
222     socklen_t length = sizeof(addr);
223     addr.sin_family = AF_INET;
224     // The address is irrelevant -- no packet is actually sent. This just needs to be a public IP.
225     addr.sin_addr.s_addr = inet_addr(8, 8, 8, 8);
226     if (connect(sock, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) == -1)
227       return {};
228     if (getsockname(sock, reinterpret_cast<sockaddr*>(&addr), &length) == -1)
229       return {};
230     return addr.sin_addr;
231   };
232 
233   auto get_addr = [](const sockaddr* addr) {
234     return reinterpret_cast<const sockaddr_in*>(addr)->sin_addr.s_addr;
235   };
236 
237   const auto default_interface_address = get_default_address();
238   if (!default_interface_address)
239     return {};
240 
241   ifaddrs* iflist;
242   if (getifaddrs(&iflist) != 0)
243     return {};
244   Common::ScopeGuard iflist_guard{[iflist] { freeifaddrs(iflist); }};
245 
246   for (const ifaddrs* iface = iflist; iface; iface = iface->ifa_next)
247   {
248     if (iface->ifa_addr && iface->ifa_addr->sa_family == AF_INET &&
249         get_addr(iface->ifa_addr) == default_interface_address->s_addr)
250     {
251       return DefaultInterface{get_addr(iface->ifa_addr), get_addr(iface->ifa_netmask),
252                               get_addr(iface->ifa_broadaddr)};
253     }
254   }
255 #endif
256   return {};
257 }
258 
GetSystemDefaultInterfaceOrFallback()259 static DefaultInterface GetSystemDefaultInterfaceOrFallback()
260 {
261   static constexpr DefaultInterface FALLBACK_VALUES{
262       inet_addr(10, 0, 1, 30), inet_addr(255, 255, 255, 0), inet_addr(10, 0, 255, 255)};
263   return GetSystemDefaultInterface().value_or(FALLBACK_VALUES);
264 }
265 
IOCtl(const IOCtlRequest & request)266 IPCCommandResult NetIPTop::IOCtl(const IOCtlRequest& request)
267 {
268   if (Core::WantsDeterminism())
269   {
270     return GetDefaultReply(IPC_EACCES);
271   }
272 
273   switch (request.request)
274   {
275   case IOCTL_SO_INITINTERFACE:
276     return HandleInitInterfaceRequest(request);
277   case IOCTL_SO_SOCKET:
278     return HandleSocketRequest(request);
279   case IOCTL_SO_ICMPSOCKET:
280     return HandleICMPSocketRequest(request);
281   case IOCTL_SO_CLOSE:
282   case IOCTL_SO_ICMPCLOSE:
283     return HandleCloseRequest(request);
284   case IOCTL_SO_ACCEPT:
285   case IOCTL_SO_BIND:
286   case IOCTL_SO_CONNECT:
287   case IOCTL_SO_FCNTL:
288     return HandleDoSockRequest(request);
289   case IOCTL_SO_SHUTDOWN:
290     return HandleShutdownRequest(request);
291   case IOCTL_SO_LISTEN:
292     return HandleListenRequest(request);
293   case IOCTL_SO_GETSOCKOPT:
294     return HandleGetSockOptRequest(request);
295   case IOCTL_SO_SETSOCKOPT:
296     return HandleSetSockOptRequest(request);
297   case IOCTL_SO_GETSOCKNAME:
298     return HandleGetSockNameRequest(request);
299   case IOCTL_SO_GETPEERNAME:
300     return HandleGetPeerNameRequest(request);
301   case IOCTL_SO_GETHOSTID:
302     return HandleGetHostIDRequest(request);
303   case IOCTL_SO_INETATON:
304     return HandleInetAToNRequest(request);
305   case IOCTL_SO_INETPTON:
306     return HandleInetPToNRequest(request);
307   case IOCTL_SO_INETNTOP:
308     return HandleInetNToPRequest(request);
309   case IOCTL_SO_POLL:
310     return HandlePollRequest(request);
311   case IOCTL_SO_GETHOSTBYNAME:
312     return HandleGetHostByNameRequest(request);
313   case IOCTL_SO_ICMPCANCEL:
314     return HandleICMPCancelRequest(request);
315   default:
316     request.DumpUnknown(GetDeviceName(), Common::Log::IOS_NET);
317     break;
318   }
319 
320   return GetDefaultReply(IPC_SUCCESS);
321 }
322 
IOCtlV(const IOCtlVRequest & request)323 IPCCommandResult NetIPTop::IOCtlV(const IOCtlVRequest& request)
324 {
325   switch (request.request)
326   {
327   case IOCTLV_SO_GETINTERFACEOPT:
328     return HandleGetInterfaceOptRequest(request);
329   case IOCTLV_SO_SENDTO:
330     return HandleSendToRequest(request);
331   case IOCTLV_SO_RECVFROM:
332     return HandleRecvFromRequest(request);
333   case IOCTLV_SO_GETADDRINFO:
334     return HandleGetAddressInfoRequest(request);
335   case IOCTLV_SO_ICMPPING:
336     return HandleICMPPingRequest(request);
337   default:
338     request.DumpUnknown(GetDeviceName(), Common::Log::IOS_NET);
339     break;
340   }
341 
342   return GetDefaultReply(IPC_SUCCESS);
343 }
344 
Update()345 void NetIPTop::Update()
346 {
347   WiiSockMan::GetInstance().Update();
348 }
349 
HandleInitInterfaceRequest(const IOCtlRequest & request)350 IPCCommandResult NetIPTop::HandleInitInterfaceRequest(const IOCtlRequest& request)
351 {
352   request.Log(GetDeviceName(), Common::Log::IOS_WC24);
353   return GetDefaultReply(IPC_SUCCESS);
354 }
355 
HandleSocketRequest(const IOCtlRequest & request)356 IPCCommandResult NetIPTop::HandleSocketRequest(const IOCtlRequest& request)
357 {
358   u32 af = Memory::Read_U32(request.buffer_in);
359   u32 type = Memory::Read_U32(request.buffer_in + 4);
360   u32 prot = Memory::Read_U32(request.buffer_in + 8);
361 
362   WiiSockMan& sm = WiiSockMan::GetInstance();
363   const s32 return_value = sm.NewSocket(af, type, prot);
364   INFO_LOG(IOS_NET,
365            "IOCTL_SO_SOCKET "
366            "Socket: %08x (%d,%d,%d), BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
367            return_value, af, type, prot, request.buffer_in, request.buffer_in_size,
368            request.buffer_out, request.buffer_out_size);
369 
370   return GetDefaultReply(return_value);
371 }
372 
HandleICMPSocketRequest(const IOCtlRequest & request)373 IPCCommandResult NetIPTop::HandleICMPSocketRequest(const IOCtlRequest& request)
374 {
375   u32 pf = Memory::Read_U32(request.buffer_in);
376 
377   WiiSockMan& sm = WiiSockMan::GetInstance();
378   const s32 return_value = sm.NewSocket(pf, SOCK_RAW, IPPROTO_ICMP);
379   INFO_LOG(IOS_NET, "IOCTL_SO_ICMPSOCKET(%x) %d", pf, return_value);
380   return GetDefaultReply(return_value);
381 }
382 
HandleCloseRequest(const IOCtlRequest & request)383 IPCCommandResult NetIPTop::HandleCloseRequest(const IOCtlRequest& request)
384 {
385   u32 fd = Memory::Read_U32(request.buffer_in);
386   WiiSockMan& sm = WiiSockMan::GetInstance();
387   const s32 return_value = sm.DeleteSocket(fd);
388   INFO_LOG(IOS_NET, "%s(%x) %x",
389            request.request == IOCTL_SO_ICMPCLOSE ? "IOCTL_SO_ICMPCLOSE" : "IOCTL_SO_CLOSE", fd,
390            return_value);
391 
392   return GetDefaultReply(return_value);
393 }
394 
HandleDoSockRequest(const IOCtlRequest & request)395 IPCCommandResult NetIPTop::HandleDoSockRequest(const IOCtlRequest& request)
396 {
397   u32 fd = Memory::Read_U32(request.buffer_in);
398   WiiSockMan& sm = WiiSockMan::GetInstance();
399   sm.DoSock(fd, request, static_cast<NET_IOCTL>(request.request));
400   return GetNoReply();
401 }
402 
HandleShutdownRequest(const IOCtlRequest & request)403 IPCCommandResult NetIPTop::HandleShutdownRequest(const IOCtlRequest& request)
404 {
405   request.Log(GetDeviceName(), Common::Log::IOS_WC24);
406 
407   u32 fd = Memory::Read_U32(request.buffer_in);
408   u32 how = Memory::Read_U32(request.buffer_in + 4);
409   int ret = shutdown(WiiSockMan::GetInstance().GetHostSocket(fd), how);
410 
411   return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_SHUTDOWN", false));
412 }
413 
HandleListenRequest(const IOCtlRequest & request)414 IPCCommandResult NetIPTop::HandleListenRequest(const IOCtlRequest& request)
415 {
416   u32 fd = Memory::Read_U32(request.buffer_in);
417   u32 BACKLOG = Memory::Read_U32(request.buffer_in + 0x04);
418   u32 ret = listen(WiiSockMan::GetInstance().GetHostSocket(fd), BACKLOG);
419 
420   request.Log(GetDeviceName(), Common::Log::IOS_WC24);
421   return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_LISTEN", false));
422 }
423 
HandleGetSockOptRequest(const IOCtlRequest & request)424 IPCCommandResult NetIPTop::HandleGetSockOptRequest(const IOCtlRequest& request)
425 {
426   u32 fd = Memory::Read_U32(request.buffer_out);
427   u32 level = Memory::Read_U32(request.buffer_out + 4);
428   u32 optname = Memory::Read_U32(request.buffer_out + 8);
429 
430   request.Log(GetDeviceName(), Common::Log::IOS_WC24);
431 
432   // Do the level/optname translation
433   int nat_level = MapWiiSockOptLevelToNative(level);
434   int nat_optname = MapWiiSockOptNameToNative(optname);
435 
436   u8 optval[20];
437   u32 optlen = 4;
438 
439   int ret = getsockopt(WiiSockMan::GetInstance().GetHostSocket(fd), nat_level, nat_optname,
440                        (char*)&optval, (socklen_t*)&optlen);
441   const s32 return_value = WiiSockMan::GetNetErrorCode(ret, "SO_GETSOCKOPT", false);
442 
443   Memory::Write_U32(optlen, request.buffer_out + 0xC);
444   Memory::CopyToEmu(request.buffer_out + 0x10, optval, optlen);
445 
446   if (optname == SO_ERROR)
447   {
448     s32 last_error = WiiSockMan::GetInstance().GetLastNetError();
449 
450     Memory::Write_U32(sizeof(s32), request.buffer_out + 0xC);
451     Memory::Write_U32(last_error, request.buffer_out + 0x10);
452   }
453 
454   return GetDefaultReply(return_value);
455 }
456 
HandleSetSockOptRequest(const IOCtlRequest & request)457 IPCCommandResult NetIPTop::HandleSetSockOptRequest(const IOCtlRequest& request)
458 {
459   u32 fd = Memory::Read_U32(request.buffer_in);
460   u32 level = Memory::Read_U32(request.buffer_in + 4);
461   u32 optname = Memory::Read_U32(request.buffer_in + 8);
462   u32 optlen = Memory::Read_U32(request.buffer_in + 0xc);
463   u8 optval[20];
464   optlen = std::min(optlen, (u32)sizeof(optval));
465   Memory::CopyFromEmu(optval, request.buffer_in + 0x10, optlen);
466 
467   INFO_LOG(IOS_NET,
468            "IOCTL_SO_SETSOCKOPT(%08x, %08x, %08x, %08x) "
469            "BufferIn: (%08x, %i), BufferOut: (%08x, %i)"
470            "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx "
471            "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx",
472            fd, level, optname, optlen, request.buffer_in, request.buffer_in_size,
473            request.buffer_out, request.buffer_out_size, optval[0], optval[1], optval[2], optval[3],
474            optval[4], optval[5], optval[6], optval[7], optval[8], optval[9], optval[10], optval[11],
475            optval[12], optval[13], optval[14], optval[15], optval[16], optval[17], optval[18],
476            optval[19]);
477 
478   // TODO: bug booto about this, 0x2005 most likely timeout related, default value on Wii is ,
479   // 0x2001 is most likely tcpnodelay
480   if (level == 6 && (optname == 0x2005 || optname == 0x2001))
481     return GetDefaultReply(0);
482 
483   // Do the level/optname translation
484   int nat_level = MapWiiSockOptLevelToNative(level);
485   int nat_optname = MapWiiSockOptNameToNative(optname);
486 
487   int ret = setsockopt(WiiSockMan::GetInstance().GetHostSocket(fd), nat_level, nat_optname,
488                        (char*)optval, optlen);
489   return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_SETSOCKOPT", false));
490 }
491 
HandleGetSockNameRequest(const IOCtlRequest & request)492 IPCCommandResult NetIPTop::HandleGetSockNameRequest(const IOCtlRequest& request)
493 {
494   u32 fd = Memory::Read_U32(request.buffer_in);
495 
496   request.Log(GetDeviceName(), Common::Log::IOS_WC24);
497 
498   sockaddr sa;
499   socklen_t sa_len = sizeof(sa);
500   int ret = getsockname(WiiSockMan::GetInstance().GetHostSocket(fd), &sa, &sa_len);
501 
502   if (request.buffer_out_size < 2 + sizeof(sa.sa_data))
503     WARN_LOG(IOS_NET, "IOCTL_SO_GETSOCKNAME output buffer is too small. Truncating");
504 
505   if (request.buffer_out_size > 0)
506     Memory::Write_U8(request.buffer_out_size, request.buffer_out);
507   if (request.buffer_out_size > 1)
508     Memory::Write_U8(sa.sa_family & 0xFF, request.buffer_out + 1);
509   if (request.buffer_out_size > 2)
510   {
511     Memory::CopyToEmu(request.buffer_out + 2, &sa.sa_data,
512                       std::min<size_t>(sizeof(sa.sa_data), request.buffer_out_size - 2));
513   }
514 
515   return GetDefaultReply(ret);
516 }
517 
HandleGetPeerNameRequest(const IOCtlRequest & request)518 IPCCommandResult NetIPTop::HandleGetPeerNameRequest(const IOCtlRequest& request)
519 {
520   u32 fd = Memory::Read_U32(request.buffer_in);
521 
522   sockaddr sa;
523   socklen_t sa_len = sizeof(sa);
524   int ret = getpeername(WiiSockMan::GetInstance().GetHostSocket(fd), &sa, &sa_len);
525 
526   if (request.buffer_out_size < 2 + sizeof(sa.sa_data))
527     WARN_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME output buffer is too small. Truncating");
528 
529   if (request.buffer_out_size > 0)
530     Memory::Write_U8(request.buffer_out_size, request.buffer_out);
531   if (request.buffer_out_size > 1)
532     Memory::Write_U8(AF_INET, request.buffer_out + 1);
533   if (request.buffer_out_size > 2)
534   {
535     Memory::CopyToEmu(request.buffer_out + 2, &sa.sa_data,
536                       std::min<size_t>(sizeof(sa.sa_data), request.buffer_out_size - 2));
537   }
538 
539   INFO_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME(%x)", fd);
540   return GetDefaultReply(ret);
541 }
542 
HandleGetHostIDRequest(const IOCtlRequest & request)543 IPCCommandResult NetIPTop::HandleGetHostIDRequest(const IOCtlRequest& request)
544 {
545   request.Log(GetDeviceName(), Common::Log::IOS_WC24);
546   const DefaultInterface interface = GetSystemDefaultInterfaceOrFallback();
547   return GetDefaultReply(Common::swap32(interface.inet));
548 }
549 
HandleInetAToNRequest(const IOCtlRequest & request)550 IPCCommandResult NetIPTop::HandleInetAToNRequest(const IOCtlRequest& request)
551 {
552   std::string hostname = Memory::GetString(request.buffer_in);
553   struct hostent* remoteHost = gethostbyname(hostname.c_str());
554 
555   if (remoteHost == nullptr || remoteHost->h_addr_list == nullptr ||
556       remoteHost->h_addr_list[0] == nullptr)
557   {
558     INFO_LOG(IOS_NET,
559              "IOCTL_SO_INETATON = -1 "
560              "%s, BufferIn: (%08x, %i), BufferOut: (%08x, %i), IP Found: None",
561              hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out,
562              request.buffer_out_size);
563     return GetDefaultReply(0);
564   }
565 
566   Memory::Write_U32(Common::swap32(*(u32*)remoteHost->h_addr_list[0]), request.buffer_out);
567   INFO_LOG(IOS_NET,
568            "IOCTL_SO_INETATON = 0 "
569            "%s, BufferIn: (%08x, %i), BufferOut: (%08x, %i), IP Found: %08X",
570            hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out,
571            request.buffer_out_size, Common::swap32(*(u32*)remoteHost->h_addr_list[0]));
572   return GetDefaultReply(1);
573 }
574 
HandleInetPToNRequest(const IOCtlRequest & request)575 IPCCommandResult NetIPTop::HandleInetPToNRequest(const IOCtlRequest& request)
576 {
577   std::string address = Memory::GetString(request.buffer_in);
578   INFO_LOG(IOS_NET, "IOCTL_SO_INETPTON (Translating: %s)", address.c_str());
579   return GetDefaultReply(inet_pton(address.c_str(), Memory::GetPointer(request.buffer_out + 4)));
580 }
581 
HandleInetNToPRequest(const IOCtlRequest & request)582 IPCCommandResult NetIPTop::HandleInetNToPRequest(const IOCtlRequest& request)
583 {
584   // u32 af = Memory::Read_U32(BufferIn);
585   // u32 validAddress = Memory::Read_U32(request.buffer_in + 4);
586   // u32 src = Memory::Read_U32(request.buffer_in + 8);
587 
588   char ip_s[16];
589   sprintf(ip_s, "%i.%i.%i.%i", Memory::Read_U8(request.buffer_in + 8),
590           Memory::Read_U8(request.buffer_in + 8 + 1), Memory::Read_U8(request.buffer_in + 8 + 2),
591           Memory::Read_U8(request.buffer_in + 8 + 3));
592 
593   INFO_LOG(IOS_NET, "IOCTL_SO_INETNTOP %s", ip_s);
594   Memory::CopyToEmu(request.buffer_out, (u8*)ip_s, strlen(ip_s));
595   return GetDefaultReply(0);
596 }
597 
HandlePollRequest(const IOCtlRequest & request)598 IPCCommandResult NetIPTop::HandlePollRequest(const IOCtlRequest& request)
599 {
600   WiiSockMan& sm = WiiSockMan::GetInstance();
601 
602   if (!request.buffer_in || !request.buffer_out)
603     return GetDefaultReply(-SO_EINVAL);
604 
605   // Negative timeout indicates wait forever
606   const s64 timeout = static_cast<s64>(Memory::Read_U64(request.buffer_in));
607 
608   const u32 nfds = request.buffer_out_size / 0xc;
609   if (nfds == 0 || nfds > WII_SOCKET_FD_MAX)
610   {
611     ERROR_LOG(IOS_NET, "IOCTL_SO_POLL failed: Invalid array size %d, ret=%d", nfds, -SO_EINVAL);
612     return GetDefaultReply(-SO_EINVAL);
613   }
614 
615   std::vector<pollfd_t> ufds(nfds);
616 
617   for (u32 i = 0; i < nfds; ++i)
618   {
619     const s32 wii_fd = Memory::Read_U32(request.buffer_out + 0xc * i);
620     ufds[i].fd = sm.GetHostSocket(wii_fd);                                  // fd
621     const int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4);  // events
622     ufds[i].revents = 0;
623 
624     // Translate Wii to native events
625     ufds[i].events = WiiSockMan::ConvertEvents(events, WiiSockMan::ConvertDirection::WiiToNative);
626     DEBUG_LOG(IOS_NET,
627               "IOCTL_SO_POLL(%d) "
628               "Sock: %08x, Events: %08x, "
629               "NativeEvents: %08x",
630               i, wii_fd, events, ufds[i].events);
631 
632     // Do not pass return-only events to the native poll
633     ufds[i].events &= ~(POLLERR | POLLHUP | POLLNVAL | UNSUPPORTED_WSAPOLL);
634   }
635 
636   // Prevents blocking emulation on a blocking poll
637   sm.AddPollCommand({request.address, request.buffer_out, std::move(ufds), timeout});
638   return GetNoReply();
639 }
640 
HandleGetHostByNameRequest(const IOCtlRequest & request)641 IPCCommandResult NetIPTop::HandleGetHostByNameRequest(const IOCtlRequest& request)
642 {
643   if (request.buffer_out_size != 0x460)
644   {
645     ERROR_LOG(IOS_NET, "Bad buffer size for IOCTL_SO_GETHOSTBYNAME");
646     return GetDefaultReply(-1);
647   }
648 
649   std::string hostname = Memory::GetString(request.buffer_in);
650   hostent* remoteHost = gethostbyname(hostname.c_str());
651 
652   INFO_LOG(IOS_NET,
653            "IOCTL_SO_GETHOSTBYNAME "
654            "Address: %s, BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
655            hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out,
656            request.buffer_out_size);
657 
658   if (remoteHost == nullptr)
659     return GetDefaultReply(-1);
660 
661   for (int i = 0; remoteHost->h_aliases[i]; ++i)
662   {
663     DEBUG_LOG(IOS_NET, "alias%i:%s", i, remoteHost->h_aliases[i]);
664   }
665 
666   for (int i = 0; remoteHost->h_addr_list[i]; ++i)
667   {
668     const u32 ip = Common::swap32(*(u32*)(remoteHost->h_addr_list[i]));
669     const std::string ip_s =
670         fmt::format("{}.{}.{}.{}", ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
671     DEBUG_LOG(IOS_NET, "addr%i:%s", i, ip_s.c_str());
672   }
673 
674   // Host name; located immediately after struct
675   static const u32 GETHOSTBYNAME_STRUCT_SIZE = 0x10;
676   static const u32 GETHOSTBYNAME_IP_LIST_OFFSET = 0x110;
677   // Limit host name length to avoid buffer overflow.
678   u32 name_length = (u32)strlen(remoteHost->h_name) + 1;
679   if (name_length > (GETHOSTBYNAME_IP_LIST_OFFSET - GETHOSTBYNAME_STRUCT_SIZE))
680   {
681     ERROR_LOG(IOS_NET, "Hostname too long in IOCTL_SO_GETHOSTBYNAME");
682     return GetDefaultReply(-1);
683   }
684   Memory::CopyToEmu(request.buffer_out + GETHOSTBYNAME_STRUCT_SIZE, remoteHost->h_name,
685                     name_length);
686   Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_STRUCT_SIZE, request.buffer_out);
687 
688   // IP address list; located at offset 0x110.
689   u32 num_ip_addr = 0;
690   while (remoteHost->h_addr_list[num_ip_addr])
691     num_ip_addr++;
692   // Limit number of IP addresses to avoid buffer overflow.
693   // (0x460 - 0x340) / sizeof(pointer) == 72
694   static const u32 GETHOSTBYNAME_MAX_ADDRESSES = 71;
695   num_ip_addr = std::min(num_ip_addr, GETHOSTBYNAME_MAX_ADDRESSES);
696   for (u32 i = 0; i < num_ip_addr; ++i)
697   {
698     u32 addr = request.buffer_out + GETHOSTBYNAME_IP_LIST_OFFSET + i * 4;
699     Memory::Write_U32_Swap(*(u32*)(remoteHost->h_addr_list[i]), addr);
700   }
701 
702   // List of pointers to IP addresses; located at offset 0x340.
703   // This must be exact: PPC code to convert the struct hardcodes
704   // this offset.
705   static const u32 GETHOSTBYNAME_IP_PTR_LIST_OFFSET = 0x340;
706   Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET, request.buffer_out + 12);
707   for (u32 i = 0; i < num_ip_addr; ++i)
708   {
709     u32 addr = request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + i * 4;
710     Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_LIST_OFFSET + i * 4, addr);
711   }
712   Memory::Write_U32(0, request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + num_ip_addr * 4);
713 
714   // Aliases - empty. (Hardware doesn't return anything.)
715   Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + num_ip_addr * 4,
716                     request.buffer_out + 4);
717 
718   // Returned struct must be ipv4.
719   ASSERT_MSG(IOS_NET, remoteHost->h_addrtype == AF_INET && remoteHost->h_length == sizeof(u32),
720              "returned host info is not IPv4");
721   Memory::Write_U16(AF_INET, request.buffer_out + 8);
722   Memory::Write_U16(sizeof(u32), request.buffer_out + 10);
723 
724   return GetDefaultReply(0);
725 }
726 
HandleICMPCancelRequest(const IOCtlRequest & request)727 IPCCommandResult NetIPTop::HandleICMPCancelRequest(const IOCtlRequest& request)
728 {
729   ERROR_LOG(IOS_NET, "IOCTL_SO_ICMPCANCEL");
730   return GetDefaultReply(0);
731 }
732 
HandleGetInterfaceOptRequest(const IOCtlVRequest & request)733 IPCCommandResult NetIPTop::HandleGetInterfaceOptRequest(const IOCtlVRequest& request)
734 {
735   const u32 param = Memory::Read_U32(request.in_vectors[0].address);
736   const u32 param2 = Memory::Read_U32(request.in_vectors[0].address + 4);
737   const u32 param3 = Memory::Read_U32(request.io_vectors[0].address);
738   const u32 param4 = Memory::Read_U32(request.io_vectors[1].address);
739   u32 param5 = 0;
740 
741   if (param != 0xfffe)
742   {
743     WARN_LOG(IOS_NET, "GetInterfaceOpt: received invalid request with param0=%08x", param);
744     return GetDefaultReply(SO_ERROR_INVALID_REQUEST);
745   }
746 
747   if (request.io_vectors[0].size >= 8)
748   {
749     param5 = Memory::Read_U32(request.io_vectors[0].address + 4);
750   }
751 
752   INFO_LOG(IOS_NET,
753            "IOCTLV_SO_GETINTERFACEOPT(%08X, %08X, %X, %X, %X) "
754            "BufferIn: (%08x, %i), BufferIn2: (%08x, %i) ",
755            param, param2, param3, param4, param5, request.in_vectors[0].address,
756            request.in_vectors[0].size,
757            request.in_vectors.size() > 1 ? request.in_vectors[1].address : 0,
758            request.in_vectors.size() > 1 ? request.in_vectors[1].size : 0);
759 
760   switch (param2)
761   {
762   case 0xb003:  // dns server table
763   {
764     const u32 default_main_dns_resolver = ntohl(::inet_addr("8.8.8.8"));
765     const u32 default_backup_dns_resolver = ntohl(::inet_addr("8.8.4.4"));
766     u32 address = 0;
767 #ifdef _WIN32
768     if (!Core::WantsDeterminism())
769     {
770       PIP_ADAPTER_ADDRESSES AdapterAddresses = nullptr;
771       ULONG OutBufferLength = 0;
772       ULONG RetVal = 0, i;
773       for (i = 0; i < 5; ++i)
774       {
775         RetVal = GetAdaptersAddresses(AF_INET, 0, nullptr, AdapterAddresses, &OutBufferLength);
776 
777         if (RetVal != ERROR_BUFFER_OVERFLOW)
778         {
779           break;
780         }
781 
782         if (AdapterAddresses != nullptr)
783         {
784           FREE(AdapterAddresses);
785         }
786 
787         AdapterAddresses = (PIP_ADAPTER_ADDRESSES)MALLOC(OutBufferLength);
788         if (AdapterAddresses == nullptr)
789         {
790           RetVal = GetLastError();
791           break;
792         }
793       }
794       if (RetVal == NO_ERROR)
795       {
796         unsigned long dwBestIfIndex = 0;
797         IPAddr dwDestAddr = static_cast<IPAddr>(default_main_dns_resolver);
798         // If successful, output some information from the data we received
799         PIP_ADAPTER_ADDRESSES AdapterList = AdapterAddresses;
800         if (GetBestInterface(dwDestAddr, &dwBestIfIndex) == NO_ERROR)
801         {
802           while (AdapterList)
803           {
804             if (AdapterList->IfIndex == dwBestIfIndex && AdapterList->FirstDnsServerAddress &&
805                 AdapterList->OperStatus == IfOperStatusUp)
806             {
807               INFO_LOG(IOS_NET, "Name of valid interface: %S", AdapterList->FriendlyName);
808               INFO_LOG(
809                   IOS_NET, "DNS: %u.%u.%u.%u",
810                   (unsigned char)AdapterList->FirstDnsServerAddress->Address.lpSockaddr->sa_data[2],
811                   (unsigned char)AdapterList->FirstDnsServerAddress->Address.lpSockaddr->sa_data[3],
812                   (unsigned char)AdapterList->FirstDnsServerAddress->Address.lpSockaddr->sa_data[4],
813                   (unsigned char)
814                       AdapterList->FirstDnsServerAddress->Address.lpSockaddr->sa_data[5]);
815               address = Common::swap32(
816                   *(u32*)(&AdapterList->FirstDnsServerAddress->Address.lpSockaddr->sa_data[2]));
817               break;
818             }
819             AdapterList = AdapterList->Next;
820           }
821         }
822       }
823       if (AdapterAddresses != nullptr)
824       {
825         FREE(AdapterAddresses);
826       }
827     }
828 #elif defined(__linux__) && !defined(__ANDROID__)
829     if (!Core::WantsDeterminism())
830     {
831       if (res_init() == 0)
832         address = ntohl(_res.nsaddr_list[0].sin_addr.s_addr);
833       else
834         WARN_LOG(IOS_NET, "Call to res_init failed");
835     }
836 #endif
837     if (address == 0)
838       address = default_main_dns_resolver;
839 
840     INFO_LOG(IOS_NET, "Primary DNS: %X", address);
841     INFO_LOG(IOS_NET, "Secondary DNS: %X", default_backup_dns_resolver);
842 
843     Memory::Write_U32(address, request.io_vectors[0].address);
844     Memory::Write_U32(default_backup_dns_resolver, request.io_vectors[0].address + 4);
845     break;
846   }
847   case 0x1003:  // error
848     Memory::Write_U32(0, request.io_vectors[0].address);
849     break;
850 
851   case 0x1004:  // mac address
852   {
853     const Common::MACAddress address = IOS::Net::GetMACAddress();
854     Memory::CopyToEmu(request.io_vectors[0].address, address.data(), address.size());
855     break;
856   }
857 
858   case 0x1005:  // link state
859     Memory::Write_U32(1, request.io_vectors[0].address);
860     break;
861 
862   case 0x3001:  // hardcoded value
863     Memory::Write_U32(0x10, request.io_vectors[0].address);
864     break;
865 
866   case 0x4002:  // ip addr numberHandle
867     Memory::Write_U32(1, request.io_vectors[0].address);
868     break;
869 
870   case 0x4003:  // ip addr table
871   {
872     // XXX: this isn't exactly right; the buffer can be larger than 12 bytes, in which case
873     // SO can write 12 more bytes.
874     Memory::Write_U32(0xC, request.io_vectors[1].address);
875     const DefaultInterface interface = GetSystemDefaultInterfaceOrFallback();
876     Memory::Write_U32(Common::swap32(interface.inet), request.io_vectors[0].address);
877     Memory::Write_U32(Common::swap32(interface.netmask), request.io_vectors[0].address + 4);
878     Memory::Write_U32(Common::swap32(interface.broadcast), request.io_vectors[0].address + 8);
879     break;
880   }
881 
882   case 0x4005:  // hardcoded value
883     Memory::Write_U32(0x20, request.io_vectors[0].address);
884     break;
885 
886   case 0x6003:  // hardcoded value
887     Memory::Write_U32(0x80, request.io_vectors[0].address);
888     break;
889 
890   case 0x600a:  // hardcoded value
891     Memory::Write_U32(0x80, request.io_vectors[0].address);
892     break;
893 
894   case 0x600c:  // hardcoded value
895     Memory::Write_U32(0x80, request.io_vectors[0].address);
896     break;
897 
898   case 0xb002:  // hardcoded value
899     Memory::Write_U32(2, request.io_vectors[0].address);
900     break;
901 
902   default:
903     ERROR_LOG(IOS_NET, "Unknown param2: %08X", param2);
904     break;
905   }
906 
907   return GetDefaultReply(0);
908 }
909 
HandleSendToRequest(const IOCtlVRequest & request)910 IPCCommandResult NetIPTop::HandleSendToRequest(const IOCtlVRequest& request)
911 {
912   u32 fd = Memory::Read_U32(request.in_vectors[1].address);
913   WiiSockMan& sm = WiiSockMan::GetInstance();
914   sm.DoSock(fd, request, IOCTLV_SO_SENDTO);
915   return GetNoReply();
916 }
917 
HandleRecvFromRequest(const IOCtlVRequest & request)918 IPCCommandResult NetIPTop::HandleRecvFromRequest(const IOCtlVRequest& request)
919 {
920   u32 fd = Memory::Read_U32(request.in_vectors[0].address);
921   WiiSockMan& sm = WiiSockMan::GetInstance();
922   sm.DoSock(fd, request, IOCTLV_SO_RECVFROM);
923   return GetNoReply();
924 }
925 
HandleGetAddressInfoRequest(const IOCtlVRequest & request)926 IPCCommandResult NetIPTop::HandleGetAddressInfoRequest(const IOCtlVRequest& request)
927 {
928   addrinfo hints;
929   const bool hints_valid = request.in_vectors.size() > 2 && request.in_vectors[2].size;
930 
931   if (hints_valid)
932   {
933     hints.ai_flags = Memory::Read_U32(request.in_vectors[2].address);
934     hints.ai_family = Memory::Read_U32(request.in_vectors[2].address + 0x4);
935     hints.ai_socktype = Memory::Read_U32(request.in_vectors[2].address + 0x8);
936     hints.ai_protocol = Memory::Read_U32(request.in_vectors[2].address + 0xC);
937     hints.ai_addrlen = Memory::Read_U32(request.in_vectors[2].address + 0x10);
938     hints.ai_canonname = nullptr;
939     hints.ai_addr = nullptr;
940     hints.ai_next = nullptr;
941   }
942 
943   // getaddrinfo allows a null pointer for the nodeName or serviceName strings
944   // So we have to do a bit of juggling here.
945   std::string nodeNameStr;
946   const char* pNodeName = nullptr;
947   if (!request.in_vectors.empty() && request.in_vectors[0].size > 0)
948   {
949     nodeNameStr = Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size);
950     pNodeName = nodeNameStr.c_str();
951   }
952 
953   std::string serviceNameStr;
954   const char* pServiceName = nullptr;
955   if (request.in_vectors.size() > 1 && request.in_vectors[1].size > 0)
956   {
957     serviceNameStr = Memory::GetString(request.in_vectors[1].address, request.in_vectors[1].size);
958     pServiceName = serviceNameStr.c_str();
959   }
960 
961   addrinfo* result = nullptr;
962   int ret = getaddrinfo(pNodeName, pServiceName, hints_valid ? &hints : nullptr, &result);
963   u32 addr = request.io_vectors[0].address;
964   u32 sockoffset = addr + 0x460;
965   if (ret == 0)
966   {
967     constexpr size_t WII_ADDR_INFO_SIZE = 0x20;
968     for (addrinfo* result_iter = result; result_iter != nullptr; result_iter = result_iter->ai_next)
969     {
970       Memory::Write_U32(result_iter->ai_flags, addr);
971       Memory::Write_U32(result_iter->ai_family, addr + 0x04);
972       Memory::Write_U32(result_iter->ai_socktype, addr + 0x08);
973       Memory::Write_U32(result_iter->ai_protocol, addr + 0x0C);
974       Memory::Write_U32((u32)result_iter->ai_addrlen, addr + 0x10);
975       // what to do? where to put? the buffer of 0x834 doesn't allow space for this
976       Memory::Write_U32(/*result->ai_cannonname*/ 0, addr + 0x14);
977 
978       if (result_iter->ai_addr)
979       {
980         Memory::Write_U32(sockoffset, addr + 0x18);
981         Memory::Write_U8(result_iter->ai_addrlen & 0xFF, sockoffset);
982         Memory::Write_U8(result_iter->ai_addr->sa_family & 0xFF, sockoffset + 0x01);
983         Memory::CopyToEmu(sockoffset + 0x2, result_iter->ai_addr->sa_data,
984                           sizeof(result_iter->ai_addr->sa_data));
985         sockoffset += 0x1C;
986       }
987       else
988       {
989         Memory::Write_U32(0, addr + 0x18);
990       }
991 
992       if (result_iter->ai_next)
993       {
994         Memory::Write_U32(addr + WII_ADDR_INFO_SIZE, addr + 0x1C);
995       }
996       else
997       {
998         Memory::Write_U32(0, addr + 0x1C);
999       }
1000 
1001       addr += WII_ADDR_INFO_SIZE;
1002     }
1003 
1004     freeaddrinfo(result);
1005   }
1006   else
1007   {
1008     ret = SO_ERROR_HOST_NOT_FOUND;
1009   }
1010 
1011   request.Dump(GetDeviceName(), Common::Log::IOS_NET, Common::Log::LINFO);
1012   return GetDefaultReply(ret);
1013 }
1014 
HandleICMPPingRequest(const IOCtlVRequest & request)1015 IPCCommandResult NetIPTop::HandleICMPPingRequest(const IOCtlVRequest& request)
1016 {
1017   struct
1018   {
1019     u8 length;
1020     u8 addr_family;
1021     u16 icmp_id;
1022     u32 ip;
1023   } ip_info;
1024 
1025   u32 fd = Memory::Read_U32(request.in_vectors[0].address);
1026   u32 num_ip = Memory::Read_U32(request.in_vectors[0].address + 4);
1027   u64 timeout = Memory::Read_U64(request.in_vectors[0].address + 8);
1028 
1029   if (num_ip != 1)
1030   {
1031     INFO_LOG(IOS_NET, "IOCTLV_SO_ICMPPING %i IPs", num_ip);
1032   }
1033 
1034   ip_info.length = Memory::Read_U8(request.in_vectors[0].address + 16);
1035   ip_info.addr_family = Memory::Read_U8(request.in_vectors[0].address + 17);
1036   ip_info.icmp_id = Memory::Read_U16(request.in_vectors[0].address + 18);
1037   ip_info.ip = Memory::Read_U32(request.in_vectors[0].address + 20);
1038 
1039   if (ip_info.length != 8 || ip_info.addr_family != AF_INET)
1040   {
1041     INFO_LOG(IOS_NET,
1042              "IOCTLV_SO_ICMPPING strange IPInfo:\n"
1043              "length %x addr_family %x",
1044              ip_info.length, ip_info.addr_family);
1045   }
1046 
1047   INFO_LOG(IOS_NET, "IOCTLV_SO_ICMPPING %x", ip_info.ip);
1048 
1049   sockaddr_in addr;
1050   addr.sin_family = AF_INET;
1051   addr.sin_addr.s_addr = Common::swap32(ip_info.ip);
1052   memset(addr.sin_zero, 0, 8);
1053 
1054   u8 data[0x20];
1055   memset(data, 0, sizeof(data));
1056   s32 icmp_length = sizeof(data);
1057 
1058   if (request.in_vectors.size() > 1 && request.in_vectors[1].size == sizeof(data))
1059   {
1060     Memory::CopyFromEmu(data, request.in_vectors[1].address, request.in_vectors[1].size);
1061   }
1062   else
1063   {
1064     // TODO sequence number is incremented either statically, by
1065     // port, or by socket. Doesn't seem to matter, so we just leave
1066     // it 0
1067     ((u16*)data)[0] = Common::swap16(ip_info.icmp_id);
1068     icmp_length = 22;
1069   }
1070 
1071   int ret = icmp_echo_req(WiiSockMan::GetInstance().GetHostSocket(fd), &addr, data, icmp_length);
1072   if (ret == icmp_length)
1073   {
1074     ret = icmp_echo_rep(WiiSockMan::GetInstance().GetHostSocket(fd), &addr,
1075                         static_cast<u32>(timeout), icmp_length);
1076   }
1077 
1078   // TODO proper error codes
1079   return GetDefaultReply(0);
1080 }
1081 }  // namespace IOS::HLE::Device
1082