Esempio n. 1
0
IPCCommandResult NetIPTop::IOCtl(const IOCtlRequest& request)
{
  if (Core::g_want_determinism)
  {
    return GetDefaultReply(IPC_EACCES);
  }

  s32 return_value = 0;
  switch (request.request)
  {
  case IOCTL_SO_STARTUP:
  {
    request.Log(GetDeviceName(), LogTypes::IOS_WC24);
    break;
  }
  case IOCTL_SO_SOCKET:
  {
    u32 af = Memory::Read_U32(request.buffer_in);
    u32 type = Memory::Read_U32(request.buffer_in + 4);
    u32 prot = Memory::Read_U32(request.buffer_in + 8);

    WiiSockMan& sm = WiiSockMan::GetInstance();
    return_value = sm.NewSocket(af, type, prot);
    INFO_LOG(IOS_NET, "IOCTL_SO_SOCKET "
                      "Socket: %08x (%d,%d,%d), BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
             return_value, af, type, prot, request.buffer_in, request.buffer_in_size,
             request.buffer_out, request.buffer_out_size);
    break;
  }
  case IOCTL_SO_ICMPSOCKET:
  {
    u32 pf = Memory::Read_U32(request.buffer_in);

    WiiSockMan& sm = WiiSockMan::GetInstance();
    return_value = sm.NewSocket(pf, SOCK_RAW, IPPROTO_ICMP);
    INFO_LOG(IOS_NET, "IOCTL_SO_ICMPSOCKET(%x) %d", pf, return_value);
    break;
  }
  case IOCTL_SO_CLOSE:
  case IOCTL_SO_ICMPCLOSE:
  {
    u32 fd = Memory::Read_U32(request.buffer_in);
    WiiSockMan& sm = WiiSockMan::GetInstance();
    return_value = sm.DeleteSocket(fd);
    INFO_LOG(IOS_NET, "%s(%x) %x",
             request.request == IOCTL_SO_ICMPCLOSE ? "IOCTL_SO_ICMPCLOSE" : "IOCTL_SO_CLOSE", fd,
             return_value);
    break;
  }
  case IOCTL_SO_ACCEPT:
  case IOCTL_SO_BIND:
  case IOCTL_SO_CONNECT:
  case IOCTL_SO_FCNTL:
  {
    u32 fd = Memory::Read_U32(request.buffer_in);
    WiiSockMan& sm = WiiSockMan::GetInstance();
    sm.DoSock(fd, request, static_cast<NET_IOCTL>(request.request));
    return GetNoReply();
  }
  /////////////////////////////////////////////////////////////
  //                  TODO: Tidy all below                   //
  /////////////////////////////////////////////////////////////
  case IOCTL_SO_SHUTDOWN:
  {
    request.Log(GetDeviceName(), LogTypes::IOS_WC24);

    u32 fd = Memory::Read_U32(request.buffer_in);
    u32 how = Memory::Read_U32(request.buffer_in + 4);
    int ret = shutdown(fd, how);
    return_value = WiiSockMan::GetNetErrorCode(ret, "SO_SHUTDOWN", false);
    break;
  }
  case IOCTL_SO_LISTEN:
  {
    u32 fd = Memory::Read_U32(request.buffer_in);
    u32 BACKLOG = Memory::Read_U32(request.buffer_in + 0x04);
    u32 ret = listen(fd, BACKLOG);
    return_value = WiiSockMan::GetNetErrorCode(ret, "SO_LISTEN", false);
    request.Log(GetDeviceName(), LogTypes::IOS_WC24);
    break;
  }
  case IOCTL_SO_GETSOCKOPT:
  {
    u32 fd = Memory::Read_U32(request.buffer_out);
    u32 level = Memory::Read_U32(request.buffer_out + 4);
    u32 optname = Memory::Read_U32(request.buffer_out + 8);

    request.Log(GetDeviceName(), LogTypes::IOS_WC24);

    // Do the level/optname translation
    int nat_level = -1, nat_optname = -1;

    for (auto& map : opt_level_mapping)
      if (level == map[1])
        nat_level = map[0];

    for (auto& map : opt_name_mapping)
      if (optname == map[1])
        nat_optname = map[0];

    u8 optval[20];
    u32 optlen = 4;

    int ret = getsockopt(fd, nat_level, nat_optname, (char*)&optval, (socklen_t*)&optlen);
    return_value = WiiSockMan::GetNetErrorCode(ret, "SO_GETSOCKOPT", false);

    Memory::Write_U32(optlen, request.buffer_out + 0xC);
    Memory::CopyToEmu(request.buffer_out + 0x10, optval, optlen);

    if (optname == SO_ERROR)
    {
      s32 last_error = WiiSockMan::GetInstance().GetLastNetError();

      Memory::Write_U32(sizeof(s32), request.buffer_out + 0xC);
      Memory::Write_U32(last_error, request.buffer_out + 0x10);
    }
    break;
  }

  case IOCTL_SO_SETSOCKOPT:
  {
    u32 fd = Memory::Read_U32(request.buffer_in);
    u32 level = Memory::Read_U32(request.buffer_in + 4);
    u32 optname = Memory::Read_U32(request.buffer_in + 8);
    u32 optlen = Memory::Read_U32(request.buffer_in + 0xc);
    u8 optval[20];
    optlen = std::min(optlen, (u32)sizeof(optval));
    Memory::CopyFromEmu(optval, request.buffer_in + 0x10, optlen);

    INFO_LOG(IOS_NET, "IOCTL_SO_SETSOCKOPT(%08x, %08x, %08x, %08x) "
                      "BufferIn: (%08x, %i), BufferOut: (%08x, %i)"
                      "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx "
                      "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx",
             fd, level, optname, optlen, request.buffer_in, request.buffer_in_size,
             request.buffer_out, request.buffer_out_size, optval[0], optval[1], optval[2],
             optval[3], optval[4], optval[5], optval[6], optval[7], optval[8], optval[9],
             optval[10], optval[11], optval[12], optval[13], optval[14], optval[15], optval[16],
             optval[17], optval[18], optval[19]);

    // TODO: bug booto about this, 0x2005 most likely timeout related, default value on Wii is ,
    // 0x2001 is most likely tcpnodelay
    if (level == 6 && (optname == 0x2005 || optname == 0x2001))
    {
      return_value = 0;
      break;
    }

    // Do the level/optname translation
    int nat_level = -1, nat_optname = -1;

    for (auto& map : opt_level_mapping)
      if (level == map[1])
        nat_level = map[0];

    for (auto& map : opt_name_mapping)
      if (optname == map[1])
        nat_optname = map[0];

    if (nat_level == -1 || nat_optname == -1)
    {
      INFO_LOG(IOS_NET, "SO_SETSOCKOPT: unknown level %d or optname %d", level, optname);

      // Default to the given level/optname. They match on Windows...
      nat_level = level;
      nat_optname = optname;
    }

    int ret = setsockopt(fd, nat_level, nat_optname, (char*)optval, optlen);
    return_value = WiiSockMan::GetNetErrorCode(ret, "SO_SETSOCKOPT", false);
    break;
  }
  case IOCTL_SO_GETSOCKNAME:
  {
    u32 fd = Memory::Read_U32(request.buffer_in);

    request.Log(GetDeviceName(), LogTypes::IOS_WC24);

    sockaddr sa;
    socklen_t sa_len;
    sa_len = sizeof(sa);
    int ret = getsockname(fd, &sa, &sa_len);

    if (request.buffer_out_size < 2 + sizeof(sa.sa_data))
      WARN_LOG(IOS_NET, "IOCTL_SO_GETSOCKNAME output buffer is too small. Truncating");

    if (request.buffer_out_size > 0)
      Memory::Write_U8(request.buffer_out_size, request.buffer_out);
    if (request.buffer_out_size > 1)
      Memory::Write_U8(sa.sa_family & 0xFF, request.buffer_out + 1);
    if (request.buffer_out_size > 2)
      Memory::CopyToEmu(request.buffer_out + 2, &sa.sa_data,
                        std::min<size_t>(sizeof(sa.sa_data), request.buffer_out_size - 2));
    return_value = ret;
    break;
  }
  case IOCTL_SO_GETPEERNAME:
  {
    u32 fd = Memory::Read_U32(request.buffer_in);

    sockaddr sa;
    socklen_t sa_len;
    sa_len = sizeof(sa);

    int ret = getpeername(fd, &sa, &sa_len);

    if (request.buffer_out_size < 2 + sizeof(sa.sa_data))
      WARN_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME output buffer is too small. Truncating");

    if (request.buffer_out_size > 0)
      Memory::Write_U8(request.buffer_out_size, request.buffer_out);
    if (request.buffer_out_size > 1)
      Memory::Write_U8(AF_INET, request.buffer_out + 1);
    if (request.buffer_out_size > 2)
      Memory::CopyToEmu(request.buffer_out + 2, &sa.sa_data,
                        std::min<size_t>(sizeof(sa.sa_data), request.buffer_out_size - 2));

    INFO_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME(%x)", fd);

    return_value = ret;
    break;
  }

  case IOCTL_SO_GETHOSTID:
  {
    request.Log(GetDeviceName(), LogTypes::IOS_WC24);

#ifdef _WIN32
    DWORD forwardTableSize, ipTableSize, result;
    DWORD ifIndex = -1;
    std::unique_ptr<MIB_IPFORWARDTABLE> forwardTable;
    std::unique_ptr<MIB_IPADDRTABLE> ipTable;

    forwardTableSize = 0;
    if (GetIpForwardTable(nullptr, &forwardTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER)
    {
      forwardTable =
          std::unique_ptr<MIB_IPFORWARDTABLE>((PMIB_IPFORWARDTABLE) operator new(forwardTableSize));
    }

    ipTableSize = 0;
    if (GetIpAddrTable(nullptr, &ipTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER)
    {
      ipTable = std::unique_ptr<MIB_IPADDRTABLE>((PMIB_IPADDRTABLE) operator new(ipTableSize));
    }

    // find the interface IP used for the default route and use that
    result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE);
    while (result == NO_ERROR ||
           result == ERROR_MORE_DATA)  // can return ERROR_MORE_DATA on XP even after the first call
    {
      for (DWORD i = 0; i < forwardTable->dwNumEntries; ++i)
      {
        if (forwardTable->table[i].dwForwardDest == 0)
        {
          ifIndex = forwardTable->table[i].dwForwardIfIndex;
          break;
        }
      }

      if (result == NO_ERROR || ifIndex != -1)
        break;

      result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE);
    }

    if (ifIndex != -1 && GetIpAddrTable(ipTable.get(), &ipTableSize, FALSE) == NO_ERROR)
    {
      for (DWORD i = 0; i < ipTable->dwNumEntries; ++i)
      {
        if (ipTable->table[i].dwIndex == ifIndex)
        {
          return_value = Common::swap32(ipTable->table[i].dwAddr);
          break;
        }
      }
    }
#endif

    // default placeholder, in case of failure
    if (return_value == 0)
      return_value = 192 << 24 | 168 << 16 | 1 << 8 | 150;
    break;
  }

  case IOCTL_SO_INETATON:
  {
    std::string hostname = Memory::GetString(request.buffer_in);
    struct hostent* remoteHost = gethostbyname(hostname.c_str());

    if (remoteHost == nullptr || remoteHost->h_addr_list == nullptr ||
        remoteHost->h_addr_list[0] == nullptr)
    {
      INFO_LOG(IOS_NET, "IOCTL_SO_INETATON = -1 "
                        "%s, BufferIn: (%08x, %i), BufferOut: (%08x, %i), IP Found: None",
               hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out,
               request.buffer_out_size);
      return_value = 0;
    }
    else
    {
      Memory::Write_U32(Common::swap32(*(u32*)remoteHost->h_addr_list[0]), request.buffer_out);
      INFO_LOG(IOS_NET, "IOCTL_SO_INETATON = 0 "
                        "%s, BufferIn: (%08x, %i), BufferOut: (%08x, %i), IP Found: %08X",
               hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out,
               request.buffer_out_size, Common::swap32(*(u32*)remoteHost->h_addr_list[0]));
      return_value = 1;
    }
    break;
  }

  case IOCTL_SO_INETPTON:
  {
    std::string address = Memory::GetString(request.buffer_in);
    INFO_LOG(IOS_NET, "IOCTL_SO_INETPTON "
                      "(Translating: %s)",
             address.c_str());
    return_value = inet_pton(address.c_str(), Memory::GetPointer(request.buffer_out + 4));
    break;
  }

  case IOCTL_SO_INETNTOP:
  {
    // u32 af = Memory::Read_U32(BufferIn);
    // u32 validAddress = Memory::Read_U32(request.buffer_in + 4);
    // u32 src = Memory::Read_U32(request.buffer_in + 8);
    char ip_s[16];
    sprintf(ip_s, "%i.%i.%i.%i", Memory::Read_U8(request.buffer_in + 8),
            Memory::Read_U8(request.buffer_in + 8 + 1), Memory::Read_U8(request.buffer_in + 8 + 2),
            Memory::Read_U8(request.buffer_in + 8 + 3));
    INFO_LOG(IOS_NET, "IOCTL_SO_INETNTOP %s", ip_s);
    Memory::CopyToEmu(request.buffer_out, (u8*)ip_s, strlen(ip_s));
    break;
  }

  case IOCTL_SO_POLL:
  {
    // Map Wii/native poll events types
    struct
    {
      int native;
      int wii;
    } mapping[] = {
        {POLLRDNORM, 0x0001}, {POLLRDBAND, 0x0002}, {POLLPRI, 0x0004}, {POLLWRNORM, 0x0008},
        {POLLWRBAND, 0x0010}, {POLLERR, 0x0020},    {POLLHUP, 0x0040}, {POLLNVAL, 0x0080},
    };

    u32 unknown = Memory::Read_U32(request.buffer_in);
    u32 timeout = Memory::Read_U32(request.buffer_in + 4);

    int nfds = request.buffer_out_size / 0xc;
    if (nfds == 0)
      ERROR_LOG(IOS_NET, "Hidden POLL");

    std::vector<pollfd_t> ufds(nfds);

    for (int i = 0; i < nfds; ++i)
    {
      ufds[i].fd = Memory::Read_U32(request.buffer_out + 0xc * i);           // fd
      int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4);       // events
      ufds[i].revents = Memory::Read_U32(request.buffer_out + 0xc * i + 8);  // revents

      // Translate Wii to native events
      int unhandled_events = events;
      ufds[i].events = 0;
      for (auto& map : mapping)
      {
        if (events & map.wii)
          ufds[i].events |= map.native;
        unhandled_events &= ~map.wii;
      }
      DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL(%d) "
                         "Sock: %08x, Unknown: %08x, Events: %08x, "
                         "NativeEvents: %08x",
                i, ufds[i].fd, unknown, events, ufds[i].events);

      // Do not pass return-only events to the native poll
      ufds[i].events &= ~(POLLERR | POLLHUP | POLLNVAL | UNSUPPORTED_WSAPOLL);

      if (unhandled_events)
        ERROR_LOG(IOS_NET, "SO_POLL: unhandled Wii event types: %04x", unhandled_events);
    }

    int ret = poll(ufds.data(), nfds, timeout);
    ret = WiiSockMan::GetNetErrorCode(ret, "SO_POLL", false);

    for (int i = 0; i < nfds; ++i)
    {
      // Translate native to Wii events
      int revents = 0;
      for (auto& map : mapping)
      {
        if (ufds[i].revents & map.native)
          revents |= map.wii;
      }

      // No need to change fd or events as they are input only.
      // Memory::Write_U32(ufds[i].fd, request.buffer_out + 0xc*i); //fd
      // Memory::Write_U32(events, request.buffer_out + 0xc*i + 4); //events
      Memory::Write_U32(revents, request.buffer_out + 0xc * i + 8);  // revents

      DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL socket %d wevents %08X events %08X revents %08X", i,
                revents, ufds[i].events, ufds[i].revents);
    }

    return_value = ret;
    break;
  }

  case IOCTL_SO_GETHOSTBYNAME:
  {
    if (request.buffer_out_size != 0x460)
    {
      ERROR_LOG(IOS_NET, "Bad buffer size for IOCTL_SO_GETHOSTBYNAME");
      return_value = -1;
      break;
    }

    std::string hostname = Memory::GetString(request.buffer_in);
    hostent* remoteHost = gethostbyname(hostname.c_str());

    INFO_LOG(IOS_NET, "IOCTL_SO_GETHOSTBYNAME "
                      "Address: %s, BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
             hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out,
             request.buffer_out_size);

    if (remoteHost)
    {
      for (int i = 0; remoteHost->h_aliases[i]; ++i)
      {
        DEBUG_LOG(IOS_NET, "alias%i:%s", i, remoteHost->h_aliases[i]);
      }

      for (int i = 0; remoteHost->h_addr_list[i]; ++i)
      {
        u32 ip = Common::swap32(*(u32*)(remoteHost->h_addr_list[i]));
        std::string ip_s = StringFromFormat("%i.%i.%i.%i", ip >> 24, (ip >> 16) & 0xff,
                                            (ip >> 8) & 0xff, ip & 0xff);
        DEBUG_LOG(IOS_NET, "addr%i:%s", i, ip_s.c_str());
      }

      // Host name; located immediately after struct
      static const u32 GETHOSTBYNAME_STRUCT_SIZE = 0x10;
      static const u32 GETHOSTBYNAME_IP_LIST_OFFSET = 0x110;
      // Limit host name length to avoid buffer overflow.
      u32 name_length = (u32)strlen(remoteHost->h_name) + 1;
      if (name_length > (GETHOSTBYNAME_IP_LIST_OFFSET - GETHOSTBYNAME_STRUCT_SIZE))
      {
        ERROR_LOG(IOS_NET, "Hostname too long in IOCTL_SO_GETHOSTBYNAME");
        return_value = -1;
        break;
      }
      Memory::CopyToEmu(request.buffer_out + GETHOSTBYNAME_STRUCT_SIZE, remoteHost->h_name,
                        name_length);
      Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_STRUCT_SIZE, request.buffer_out);

      // IP address list; located at offset 0x110.
      u32 num_ip_addr = 0;
      while (remoteHost->h_addr_list[num_ip_addr])
        num_ip_addr++;
      // Limit number of IP addresses to avoid buffer overflow.
      // (0x460 - 0x340) / sizeof(pointer) == 72
      static const u32 GETHOSTBYNAME_MAX_ADDRESSES = 71;
      num_ip_addr = std::min(num_ip_addr, GETHOSTBYNAME_MAX_ADDRESSES);
      for (u32 i = 0; i < num_ip_addr; ++i)
      {
        u32 addr = request.buffer_out + GETHOSTBYNAME_IP_LIST_OFFSET + i * 4;
        Memory::Write_U32_Swap(*(u32*)(remoteHost->h_addr_list[i]), addr);
      }

      // List of pointers to IP addresses; located at offset 0x340.
      // This must be exact: PPC code to convert the struct hardcodes
      // this offset.
      static const u32 GETHOSTBYNAME_IP_PTR_LIST_OFFSET = 0x340;
      Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET,
                        request.buffer_out + 12);
      for (u32 i = 0; i < num_ip_addr; ++i)
      {
        u32 addr = request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + i * 4;
        Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_LIST_OFFSET + i * 4, addr);
      }
      Memory::Write_U32(0, request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + num_ip_addr * 4);

      // Aliases - empty. (Hardware doesn't return anything.)
      Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + num_ip_addr * 4,
                        request.buffer_out + 4);

      // Returned struct must be ipv4.
      _assert_msg_(IOS_NET,
                   remoteHost->h_addrtype == AF_INET && remoteHost->h_length == sizeof(u32),
                   "returned host info is not IPv4");
      Memory::Write_U16(AF_INET, request.buffer_out + 8);
      Memory::Write_U16(sizeof(u32), request.buffer_out + 10);

      return_value = 0;
    }
    else
    {
      return_value = -1;
    }

    break;
  }

  case IOCTL_SO_ICMPCANCEL:
    ERROR_LOG(IOS_NET, "IOCTL_SO_ICMPCANCEL");

  default:
    request.DumpUnknown(GetDeviceName(), LogTypes::IOS_NET);
  }
Esempio n. 2
0
IPCCommandResult NetIPTop::HandlePollRequest(const IOCtlRequest& request)
{
  // Map Wii/native poll events types
  struct
  {
    int native;
    int wii;
  } mapping[] = {
      {POLLRDNORM, 0x0001}, {POLLRDBAND, 0x0002}, {POLLPRI, 0x0004}, {POLLWRNORM, 0x0008},
      {POLLWRBAND, 0x0010}, {POLLERR, 0x0020},    {POLLHUP, 0x0040}, {POLLNVAL, 0x0080},
  };

  u32 unknown = Memory::Read_U32(request.buffer_in);
  u32 timeout = Memory::Read_U32(request.buffer_in + 4);

  int nfds = request.buffer_out_size / 0xc;
  if (nfds == 0)
    ERROR_LOG(IOS_NET, "Hidden POLL");

  std::vector<pollfd_t> ufds(nfds);

  for (int i = 0; i < nfds; ++i)
  {
    s32 wii_fd = Memory::Read_U32(request.buffer_out + 0xc * i);
    ufds[i].fd = WiiSockMan::GetInstance().GetHostSocket(wii_fd);          // fd
    int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4);       // events
    ufds[i].revents = Memory::Read_U32(request.buffer_out + 0xc * i + 8);  // revents

    // Translate Wii to native events
    int unhandled_events = events;
    ufds[i].events = 0;
    for (auto& map : mapping)
    {
      if (events & map.wii)
        ufds[i].events |= map.native;
      unhandled_events &= ~map.wii;
    }
    DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL(%d) "
                       "Sock: %08x, Unknown: %08x, Events: %08x, "
                       "NativeEvents: %08x",
              i, wii_fd, unknown, events, ufds[i].events);

    // Do not pass return-only events to the native poll
    ufds[i].events &= ~(POLLERR | POLLHUP | POLLNVAL | UNSUPPORTED_WSAPOLL);

    if (unhandled_events)
      ERROR_LOG(IOS_NET, "SO_POLL: unhandled Wii event types: %04x", unhandled_events);
  }

  int ret = poll(ufds.data(), nfds, timeout);
  ret = WiiSockMan::GetNetErrorCode(ret, "SO_POLL", false);

  for (int i = 0; i < nfds; ++i)
  {
    // Translate native to Wii events
    int revents = 0;
    for (auto& map : mapping)
    {
      if (ufds[i].revents & map.native)
        revents |= map.wii;
    }

    // No need to change fd or events as they are input only.
    // Memory::Write_U32(ufds[i].fd, request.buffer_out + 0xc*i); //fd
    // Memory::Write_U32(events, request.buffer_out + 0xc*i + 4); //events
    Memory::Write_U32(revents, request.buffer_out + 0xc * i + 8);  // revents

    DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL socket %d wevents %08X events %08X revents %08X", i, revents,
              ufds[i].events, ufds[i].revents);
  }

  return GetDefaultReply(ret);
}