IPCCommandResult FileIO::IOCtl(const IOCtlRequest& request) { DEBUG_LOG(IOS_FILEIO, "FileIO: IOCtl (Device=%s)", m_name.c_str()); s32 return_value = IPC_SUCCESS; switch (request.request) { case ISFS_IOCTL_GETFILESTATS: { if (m_file->IsOpen()) { DEBUG_LOG(IOS_FILEIO, "File: %s, Length: %" PRIu64 ", Pos: %i", m_name.c_str(), m_file->GetSize(), m_SeekPos); Memory::Write_U32(static_cast<u32>(m_file->GetSize()), request.buffer_out); Memory::Write_U32(m_SeekPos, request.buffer_out + 4); } else { return_value = FS_ENOENT; } } break; default: request.Log(GetDeviceName(), LogTypes::IOS_FILEIO, LogTypes::LERROR); } return GetDefaultReply(return_value); }
IPCCommandResult NetIPTop::HandleGetSockNameRequest(const IOCtlRequest& request) { u32 fd = Memory::Read_U32(request.buffer_in); request.Log(GetDeviceName(), LogTypes::IOS_WC24); sockaddr sa; socklen_t sa_len = sizeof(sa); int ret = getsockname(WiiSockMan::GetInstance().GetHostSocket(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 GetDefaultReply(ret); }
IPCCommandResult NetIPTop::HandleGetSockOptRequest(const IOCtlRequest& request) { 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 = MapWiiSockOptLevelToNative(level); int nat_optname = MapWiiSockOptNameToNative(optname); u8 optval[20]; u32 optlen = 4; int ret = getsockopt(WiiSockMan::GetInstance().GetHostSocket(fd), nat_level, nat_optname, (char*)&optval, (socklen_t*)&optlen); const s32 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); } return GetDefaultReply(return_value); }
IPCCommandResult NetIPTop::HandleListenRequest(const IOCtlRequest& request) { u32 fd = Memory::Read_U32(request.buffer_in); u32 BACKLOG = Memory::Read_U32(request.buffer_in + 0x04); u32 ret = listen(WiiSockMan::GetInstance().GetHostSocket(fd), BACKLOG); request.Log(GetDeviceName(), LogTypes::IOS_WC24); return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_LISTEN", false)); }
IPCCommandResult NetIPTop::HandleShutdownRequest(const IOCtlRequest& request) { 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(WiiSockMan::GetInstance().GetHostSocket(fd), how); return GetDefaultReply(WiiSockMan::GetNetErrorCode(ret, "SO_SHUTDOWN", false)); }
IPCCommandResult STMImmediate::IOCtl(const IOCtlRequest& request) { s32 return_value = IPC_SUCCESS; switch (request.request) { case IOCTL_STM_IDLE: case IOCTL_STM_SHUTDOWN: NOTICE_LOG(IOS_STM, "IOCTL_STM_IDLE or IOCTL_STM_SHUTDOWN received, shutting down"); Core::QueueHostJob(&Core::Stop, false); break; case IOCTL_STM_RELEASE_EH: if (!s_event_hook_request) { return_value = IPC_ENOENT; break; } Memory::Write_U32(0, s_event_hook_request->buffer_out); EnqueueReply(*s_event_hook_request, IPC_SUCCESS); s_event_hook_request.reset(); break; case IOCTL_STM_HOTRESET: INFO_LOG(IOS_STM, "%s - IOCtl:", GetDeviceName().c_str()); INFO_LOG(IOS_STM, " IOCTL_STM_HOTRESET"); break; case IOCTL_STM_VIDIMMING: // (Input: 20 bytes, Output: 20 bytes) INFO_LOG(IOS_STM, "%s - IOCtl:", GetDeviceName().c_str()); INFO_LOG(IOS_STM, " IOCTL_STM_VIDIMMING"); // Memory::Write_U32(1, buffer_out); // return_value = 1; break; case IOCTL_STM_LEDMODE: // (Input: 20 bytes, Output: 20 bytes) INFO_LOG(IOS_STM, "%s - IOCtl:", GetDeviceName().c_str()); INFO_LOG(IOS_STM, " IOCTL_STM_LEDMODE"); break; default: request.DumpUnknown(GetDeviceName(), LogTypes::IOS_STM); } return GetDefaultReply(return_value); }
IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request) { s32 return_error_code = IPC_SUCCESS; switch (request.request) { case IOCTL_WFSI_IMPORT_TITLE_INIT: { u32 tmd_addr = Memory::Read_U32(request.buffer_in); u32 tmd_size = Memory::Read_U32(request.buffer_in + 4); m_patch_type = static_cast<PatchType>(Memory::Read_U32(request.buffer_in + 32)); m_continue_install = Memory::Read_U32(request.buffer_in + 36); INFO_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_INIT: patch type %d, continue install: %s", m_patch_type, m_continue_install ? "true" : "false"); if (m_patch_type == PatchType::PATCH_TYPE_2) { const std::string content_dir = StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(), m_current_group_id_str.c_str(), m_current_title_id_str.c_str()); File::Rename(WFS::NativePath(content_dir + "/default.dol"), WFS::NativePath(content_dir + "/_default.dol")); } if (!IOS::ES::IsValidTMDSize(tmd_size)) { ERROR_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_INIT: TMD size too large (%d)", tmd_size); return_error_code = IPC_EINVAL; break; } std::vector<u8> tmd_bytes; tmd_bytes.resize(tmd_size); Memory::CopyFromEmu(tmd_bytes.data(), tmd_addr, tmd_size); m_tmd.SetBytes(std::move(tmd_bytes)); IOS::ES::TicketReader ticket = m_ios.GetES()->FindSignedTicket(m_tmd.GetTitleId()); if (!ticket.IsValid()) { return_error_code = -11028; break; } memcpy(m_aes_key, ticket.GetTitleKey(m_ios.GetIOSC()).data(), sizeof(m_aes_key)); mbedtls_aes_setkey_dec(&m_aes_ctx, m_aes_key, 128); SetImportTitleIdAndGroupId(m_tmd.GetTitleId(), m_tmd.GetGroupId()); if (m_patch_type == PatchType::PATCH_TYPE_1) CancelPatchImport(m_continue_install); else if (m_patch_type == PatchType::NOT_A_PATCH) CancelTitleImport(m_continue_install); break; } case IOCTL_WFSI_PREPARE_PROFILE: m_base_extract_path = StringFromFormat("/vol/%s/tmp/", m_device_name.c_str()); // Fall through intended. case IOCTL_WFSI_PREPARE_CONTENT: { const char* ioctl_name = request.request == IOCTL_WFSI_PREPARE_PROFILE ? "IOCTL_WFSI_PREPARE_PROFILE" : "IOCTL_WFSI_PREPARE_CONTENT"; // Initializes the IV from the index of the content in the TMD contents. u32 content_id = Memory::Read_U32(request.buffer_in + 8); IOS::ES::Content content_info; if (!m_tmd.FindContentById(content_id, &content_info)) { WARN_LOG(IOS_WFS, "%s: Content id %08x not found", ioctl_name, content_id); return_error_code = -10003; break; } memset(m_aes_iv, 0, sizeof(m_aes_iv)); m_aes_iv[0] = content_info.index >> 8; m_aes_iv[1] = content_info.index & 0xFF; INFO_LOG(IOS_WFS, "%s: Content id %08x found at index %d", ioctl_name, content_id, content_info.index); m_arc_unpacker.Reset(); break; } case IOCTL_WFSI_IMPORT_PROFILE: case IOCTL_WFSI_IMPORT_CONTENT: { const char* ioctl_name = request.request == IOCTL_WFSI_IMPORT_PROFILE ? "IOCTL_WFSI_IMPORT_PROFILE" : "IOCTL_WFSI_IMPORT_CONTENT"; u32 content_id = Memory::Read_U32(request.buffer_in + 0xC); u32 input_ptr = Memory::Read_U32(request.buffer_in + 0x10); u32 input_size = Memory::Read_U32(request.buffer_in + 0x14); INFO_LOG(IOS_WFS, "%s: %08x bytes of data at %08x from content id %d", ioctl_name, input_size, input_ptr, content_id); std::vector<u8> decrypted(input_size); mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, input_size, m_aes_iv, Memory::GetPointer(input_ptr), decrypted.data()); m_arc_unpacker.AddBytes(decrypted); break; } case IOCTL_WFSI_IMPORT_CONTENT_END: case IOCTL_WFSI_IMPORT_PROFILE_END: { const char* ioctl_name = request.request == IOCTL_WFSI_IMPORT_PROFILE_END ? "IOCTL_WFSI_IMPORT_PROFILE_END" : "IOCTL_WFSI_IMPORT_CONTENT_END"; INFO_LOG(IOS_WFS, "%s", ioctl_name); auto callback = [this](const std::string& filename, const std::vector<u8>& bytes) { INFO_LOG(IOS_WFS, "Extract: %s (%zd bytes)", filename.c_str(), bytes.size()); std::string path = WFS::NativePath(m_base_extract_path + "/" + filename); File::CreateFullPath(path); File::IOFile f(path, "wb"); if (!f) { ERROR_LOG(IOS_WFS, "Could not extract %s to %s", filename.c_str(), path.c_str()); return; } f.WriteBytes(bytes.data(), bytes.size()); }; m_arc_unpacker.Extract(callback); // Technically not needed, but let's not keep large buffers in RAM for no // reason if we can avoid it. m_arc_unpacker.Reset(); break; } case IOCTL_WFSI_FINALIZE_TITLE_INSTALL: { std::string tmd_path; if (m_patch_type == NOT_A_PATCH) { std::string title_install_dir = StringFromFormat("/vol/%s/_install/%s", m_device_name.c_str(), m_import_title_id_str.c_str()); std::string title_final_dir = StringFromFormat("/vol/%s/title/%s/%s", m_device_name.c_str(), m_import_group_id_str.c_str(), m_import_title_id_str.c_str()); File::Rename(WFS::NativePath(title_install_dir), WFS::NativePath(title_final_dir)); tmd_path = StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd", m_device_name.c_str(), m_import_group_id_str.c_str(), m_import_title_id_str.c_str(), m_import_title_id); } else { std::string patch_dir = StringFromFormat("/vol/%s/title/%s/%s/_patch", m_device_name.c_str(), m_current_group_id_str.c_str(), m_current_title_id_str.c_str()); File::DeleteDirRecursively(WFS::NativePath(patch_dir)); tmd_path = StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd", m_device_name.c_str(), m_current_group_id_str.c_str(), m_current_title_id_str.c_str(), m_import_title_id); } File::IOFile tmd_file(WFS::NativePath(tmd_path), "wb"); tmd_file.WriteBytes(m_tmd.GetBytes().data(), m_tmd.GetBytes().size()); break; } case IOCTL_WFSI_FINALIZE_PATCH_INSTALL: { INFO_LOG(IOS_WFS, "IOCTL_WFSI_FINALIZE_PATCH_INSTALL"); if (m_patch_type != NOT_A_PATCH) { std::string current_title_dir = StringFromFormat("/vol/%s/title/%s/%s", m_device_name.c_str(), m_current_group_id_str.c_str(), m_current_title_id_str.c_str()); std::string patch_dir = current_title_dir + "/_patch"; File::CopyDir(WFS::NativePath(patch_dir), WFS::NativePath(current_title_dir), true); } break; } case IOCTL_WFSI_DELETE_TITLE: // Bytes 0-4: ?? // Bytes 4-8: game id // Bytes 1c-1e: title id? WARN_LOG(IOS_WFS, "IOCTL_WFSI_DELETE_TITLE: unimplemented"); break; case IOCTL_WFSI_GET_VERSION: INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_VERSION"); Memory::Write_U32(0x20, request.buffer_out); break; case IOCTL_WFSI_IMPORT_TITLE_CANCEL: { INFO_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_CANCEL"); bool continue_install = Memory::Read_U32(request.buffer_in) != 0; if (m_patch_type == PatchType::NOT_A_PATCH) return_error_code = CancelTitleImport(continue_install); else if (m_patch_type == PatchType::PATCH_TYPE_1 || m_patch_type == PatchType::PATCH_TYPE_2) return_error_code = CancelPatchImport(continue_install); else return_error_code = WFS_EINVAL; m_tmd = {}; break; } case IOCTL_WFSI_INIT: { INFO_LOG(IOS_WFS, "IOCTL_WFSI_INIT"); u64 tid; if (GetIOS()->GetES()->GetTitleId(&tid) < 0) { ERROR_LOG(IOS_WFS, "IOCTL_WFSI_INIT: Could not get title id."); return_error_code = IPC_EINVAL; break; } IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(tid); SetCurrentTitleIdAndGroupId(tmd.GetTitleId(), tmd.GetGroupId()); break; } case IOCTL_WFSI_SET_DEVICE_NAME: INFO_LOG(IOS_WFS, "IOCTL_WFSI_SET_DEVICE_NAME"); m_device_name = Memory::GetString(request.buffer_in); break; case IOCTL_WFSI_APPLY_TITLE_PROFILE: { INFO_LOG(IOS_WFS, "IOCTL_WFSI_APPLY_TITLE_PROFILE"); if (m_patch_type == NOT_A_PATCH) { std::string install_directory = StringFromFormat("/vol/%s/_install", m_device_name.c_str()); if (!m_continue_install && File::IsDirectory(WFS::NativePath(install_directory))) { File::DeleteDirRecursively(WFS::NativePath(install_directory)); } m_base_extract_path = StringFromFormat("%s/%s/content", install_directory.c_str(), m_import_title_id_str.c_str()); File::CreateFullPath(WFS::NativePath(m_base_extract_path)); File::CreateDir(WFS::NativePath(m_base_extract_path)); for (auto dir : {"work", "meta", "save"}) { std::string path = StringFromFormat("%s/%s/%s", install_directory.c_str(), m_import_title_id_str.c_str(), dir); File::CreateDir(WFS::NativePath(path)); } std::string group_path = StringFromFormat("/vol/%s/title/%s", m_device_name.c_str(), m_import_group_id_str.c_str()); File::CreateFullPath(WFS::NativePath(group_path)); File::CreateDir(WFS::NativePath(group_path)); } else { m_base_extract_path = StringFromFormat("/vol/%s/title/%s/%s/_patch/content", m_device_name.c_str(), m_current_group_id_str.c_str(), m_current_title_id_str.c_str()); File::CreateFullPath(WFS::NativePath(m_base_extract_path)); File::CreateDir(WFS::NativePath(m_base_extract_path)); } break; } case IOCTL_WFSI_GET_TMD: { u64 subtitle_id = Memory::Read_U64(request.buffer_in); u32 address = Memory::Read_U32(request.buffer_in + 24); INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_TMD: subtitle ID %016" PRIx64, subtitle_id); u32 tmd_size; return_error_code = GetTmd(m_current_group_id, m_current_title_id, subtitle_id, address, &tmd_size); Memory::Write_U32(tmd_size, request.buffer_out); break; } case IOCTL_WFSI_GET_TMD_ABSOLUTE: { u64 subtitle_id = Memory::Read_U64(request.buffer_in); u32 address = Memory::Read_U32(request.buffer_in + 24); u16 group_id = Memory::Read_U16(request.buffer_in + 36); u32 title_id = Memory::Read_U32(request.buffer_in + 32); INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_TMD_ABSOLUTE: tid %08x, gid %04x, subtitle ID %016" PRIx64, title_id, group_id, subtitle_id); u32 tmd_size; return_error_code = GetTmd(group_id, title_id, subtitle_id, address, &tmd_size); Memory::Write_U32(tmd_size, request.buffer_out); break; } case IOCTL_WFSI_SET_FST_BUFFER: { INFO_LOG(IOS_WFS, "IOCTL_WFSI_SET_FST_BUFFER: address %08x, size %08x", request.buffer_in, request.buffer_in_size); break; } case IOCTL_WFSI_NOOP: break; case IOCTL_WFSI_LOAD_DOL: { std::string path = StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(), m_current_group_id_str.c_str(), m_current_title_id_str.c_str()); u32 dol_addr = Memory::Read_U32(request.buffer_in + 0x18); u32 max_dol_size = Memory::Read_U32(request.buffer_in + 0x14); u16 dol_extension_id = Memory::Read_U16(request.buffer_in + 0x1e); if (dol_extension_id == 0) { path += "/default.dol"; } else { path += StringFromFormat("/extension%d.dol", dol_extension_id); } INFO_LOG(IOS_WFS, "IOCTL_WFSI_LOAD_DOL: loading %s at address %08x (size %d)", path.c_str(), dol_addr, max_dol_size); File::IOFile fp(WFS::NativePath(path), "rb"); if (!fp) { WARN_LOG(IOS_WFS, "IOCTL_WFSI_LOAD_DOL: no such file or directory: %s", path.c_str()); return_error_code = WFS_ENOENT; break; } u32 real_dol_size = fp.GetSize(); if (dol_addr == 0) { // Write the expected size to the size parameter, in the input. Memory::Write_U32(real_dol_size, request.buffer_in + 0x14); } else { fp.ReadBytes(Memory::GetPointer(dol_addr), max_dol_size); } Memory::Write_U32(real_dol_size, request.buffer_out); break; } case IOCTL_WFSI_CHECK_HAS_SPACE: WARN_LOG(IOS_WFS, "IOCTL_WFSI_CHECK_HAS_SPACE: returning true"); // TODO(wfs): implement this properly. // 1 is returned if there is free space, 0 otherwise. // // WFSI builds a path depending on the import state // /vol/VOLUME_ID/title/GROUP_ID/GAME_ID // /vol/VOLUME_ID/_install/GAME_ID // then removes everything after the last path separator ('/') // it then calls WFSISrvGetFreeBlkNum (ioctl 0x5a, aliased to 0x5b) with that path. // If the ioctl fails, WFSI returns 0. // If the ioctl succeeds, WFSI returns 0 or 1 depending on the three u32s in the input buffer // and the three u32s returned by WFSSRV (TODO: figure out what it does) return_error_code = 1; break; default: // TODO(wfs): Should be returning an error. However until we have // everything properly stubbed it's easier to simulate the methods // succeeding. request.DumpUnknown(GetDeviceName(), LogTypes::IOS, LogTypes::LWARNING); Memory::Memset(request.buffer_out, 0, request.buffer_out_size); break; } return GetDefaultReply(return_error_code); }
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); }
IPCCommandResult NetIPTop::HandleGetHostIDRequest(const IOCtlRequest& request) { request.Log(GetDeviceName(), LogTypes::IOS_WC24); s32 return_value = 0; #ifdef _WIN32 DWORD forwardTableSize, ipTableSize, result; NET_IFINDEX ifIndex = NET_IFINDEX_UNSPECIFIED; 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); // can return ERROR_MORE_DATA on XP even after the first call while (result == NO_ERROR || result == ERROR_MORE_DATA) { 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 != NET_IFINDEX_UNSPECIFIED) break; result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE); } if (ifIndex != NET_IFINDEX_UNSPECIFIED && 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; return GetDefaultReply(return_value); }
IPCCommandResult NetIPTop::HandleStartUpRequest(const IOCtlRequest& request) { request.Log(GetDeviceName(), LogTypes::IOS_WC24); return GetDefaultReply(IPC_SUCCESS); }