Result<Metadata> HostFileSystem::GetMetadata(Uid, Gid, const std::string& path) { Metadata metadata; metadata.uid = 0; metadata.gid = 0x3031; // this is also known as makercd, 01 (0x3031) for nintendo and 08 // (0x3038) for MH3 etc if (!IsValidWiiPath(path)) return ResultCode::Invalid; std::string file_name = BuildFilename(path); metadata.modes = {Mode::ReadWrite, Mode::ReadWrite, Mode::ReadWrite}; metadata.attribute = 0x00; // no attributes // Hack: if the path that is being accessed is within an installed title directory, get the // UID/GID from the installed title TMD. Kernel* ios = GetIOS(); u64 title_id; if (ios && IsTitlePath(file_name, Common::FROM_SESSION_ROOT, &title_id)) { IOS::ES::TMDReader tmd = ios->GetES()->FindInstalledTMD(title_id); if (tmd.IsValid()) metadata.gid = tmd.GetGroupId(); } const File::FileInfo info{file_name}; metadata.is_file = info.IsFile(); metadata.size = info.GetSize(); if (!info.Exists()) return ResultCode::NotFound; return metadata; }
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); }
void WiiSocket::Update(bool read, bool write, bool except) { auto it = pending_sockops.begin(); while (it != pending_sockops.end()) { s32 ReturnValue = 0; bool forceNonBlock = false; IPCCommandType ct = it->request.command; if (!it->is_ssl && ct == IPC_CMD_IOCTL) { IOCtlRequest ioctl{it->request.address}; switch (it->net_type) { case IOCTL_SO_FCNTL: { u32 cmd = Memory::Read_U32(ioctl.buffer_in + 4); u32 arg = Memory::Read_U32(ioctl.buffer_in + 8); ReturnValue = FCntl(cmd, arg); break; } case IOCTL_SO_BIND: { sockaddr_in local_name; WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(ioctl.buffer_in + 8); WiiSockMan::Convert(*wii_name, local_name); int ret = bind(fd, (sockaddr*)&local_name, sizeof(local_name)); ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_BIND", false); INFO_LOG(IOS_NET, "IOCTL_SO_BIND (%08X, %s:%d) = %d", wii_fd, inet_ntoa(local_name.sin_addr), Common::swap16(local_name.sin_port), ret); break; } case IOCTL_SO_CONNECT: { sockaddr_in local_name; WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(ioctl.buffer_in + 8); WiiSockMan::Convert(*wii_name, local_name); int ret = connect(fd, (sockaddr*)&local_name, sizeof(local_name)); ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_CONNECT", false); INFO_LOG(IOS_NET, "IOCTL_SO_CONNECT (%08x, %s:%d) = %d", wii_fd, inet_ntoa(local_name.sin_addr), Common::swap16(local_name.sin_port), ret); break; } case IOCTL_SO_ACCEPT: { s32 ret; if (ioctl.buffer_out_size > 0) { sockaddr_in local_name; WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(ioctl.buffer_out); WiiSockMan::Convert(*wii_name, local_name); socklen_t addrlen = sizeof(sockaddr_in); ret = static_cast<s32>(accept(fd, (sockaddr*)&local_name, &addrlen)); WiiSockMan::Convert(local_name, *wii_name, addrlen); } else { ret = static_cast<s32>(accept(fd, nullptr, nullptr)); } ReturnValue = WiiSockMan::GetInstance().AddSocket(ret, true); ioctl.Log("IOCTL_SO_ACCEPT", LogTypes::IOS_NET); break; } default: break; } // Fix blocking error codes if (!nonBlock) { if (it->net_type == IOCTL_SO_CONNECT && ReturnValue == -SO_EISCONN) { ReturnValue = SO_SUCCESS; } } } else if (ct == IPC_CMD_IOCTLV) { IOCtlVRequest ioctlv{it->request.address}; u32 BufferIn = 0, BufferIn2 = 0; u32 BufferInSize = 0, BufferInSize2 = 0; u32 BufferOut = 0, BufferOut2 = 0; u32 BufferOutSize = 0, BufferOutSize2 = 0; if (ioctlv.in_vectors.size() > 0) { BufferIn = ioctlv.in_vectors.at(0).address; BufferInSize = ioctlv.in_vectors.at(0).size; } if (ioctlv.io_vectors.size() > 0) { BufferOut = ioctlv.io_vectors.at(0).address; BufferOutSize = ioctlv.io_vectors.at(0).size; } if (ioctlv.io_vectors.size() > 1) { BufferOut2 = ioctlv.io_vectors.at(1).address; BufferOutSize2 = ioctlv.io_vectors.at(1).size; } if (ioctlv.in_vectors.size() > 1) { BufferIn2 = ioctlv.in_vectors.at(1).address; BufferInSize2 = ioctlv.in_vectors.at(1).size; } if (it->is_ssl) { int sslID = Memory::Read_U32(BufferOut) - 1; if (SSLID_VALID(sslID)) { switch (it->ssl_type) { case IOCTLV_NET_SSL_DOHANDSHAKE: { mbedtls_ssl_context* ctx = &Device::NetSSL::_SSL[sslID].ctx; int ret = mbedtls_ssl_handshake(ctx); if (ret) { char error_buffer[256] = ""; mbedtls_strerror(ret, error_buffer, sizeof(error_buffer)); ERROR_LOG(IOS_SSL, "IOCTLV_NET_SSL_DOHANDSHAKE: %s", error_buffer); } switch (ret) { case 0: WriteReturnValue(SSL_OK, BufferIn); break; case MBEDTLS_ERR_SSL_WANT_READ: WriteReturnValue(SSL_ERR_RAGAIN, BufferIn); if (!nonBlock) ReturnValue = SSL_ERR_RAGAIN; break; case MBEDTLS_ERR_SSL_WANT_WRITE: WriteReturnValue(SSL_ERR_WAGAIN, BufferIn); if (!nonBlock) ReturnValue = SSL_ERR_WAGAIN; break; case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: { char error_buffer[256] = ""; int res = mbedtls_ssl_get_verify_result(ctx); mbedtls_x509_crt_verify_info(error_buffer, sizeof(error_buffer), "", res); ERROR_LOG(IOS_SSL, "MBEDTLS_ERR_X509_CERT_VERIFY_FAILED (verify_result = %d): %s", res, error_buffer); if (res & MBEDTLS_X509_BADCERT_CN_MISMATCH) res = SSL_ERR_VCOMMONNAME; else if (res & MBEDTLS_X509_BADCERT_NOT_TRUSTED) res = SSL_ERR_VROOTCA; else if (res & MBEDTLS_X509_BADCERT_REVOKED) res = SSL_ERR_VCHAIN; else if (res & MBEDTLS_X509_BADCERT_EXPIRED || res & MBEDTLS_X509_BADCERT_FUTURE) res = SSL_ERR_VDATE; else res = SSL_ERR_FAILED; WriteReturnValue(res, BufferIn); if (!nonBlock) ReturnValue = res; break; } default: WriteReturnValue(SSL_ERR_FAILED, BufferIn); break; } // mbedtls_ssl_get_peer_cert(ctx) seems not to work if handshake failed // Below is an alternative to dump the peer certificate if (SConfig::GetInstance().m_SSLDumpPeerCert && ctx->session_negotiate != nullptr) { const mbedtls_x509_crt* cert = ctx->session_negotiate->peer_cert; if (cert != nullptr) { std::string filename = File::GetUserPath(D_DUMPSSL_IDX) + ((ctx->hostname != nullptr) ? ctx->hostname : "") + "_peercert.der"; File::IOFile(filename, "wb").WriteBytes(cert->raw.p, cert->raw.len); } } INFO_LOG(IOS_SSL, "IOCTLV_NET_SSL_DOHANDSHAKE = (%d) " "BufferIn: (%08x, %i), BufferIn2: (%08x, %i), " "BufferOut: (%08x, %i), BufferOut2: (%08x, %i)", ret, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut, BufferOutSize, BufferOut2, BufferOutSize2); break; } case IOCTLV_NET_SSL_WRITE: { int ret = mbedtls_ssl_write(&Device::NetSSL::_SSL[sslID].ctx, Memory::GetPointer(BufferOut2), BufferOutSize2); if (SConfig::GetInstance().m_SSLDumpWrite && ret > 0) { std::string filename = File::GetUserPath(D_DUMPSSL_IDX) + SConfig::GetInstance().GetGameID() + "_write.bin"; File::IOFile(filename, "ab").WriteBytes(Memory::GetPointer(BufferOut2), ret); } if (ret >= 0) { // Return bytes written or SSL_ERR_ZERO if none WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn); } else { switch (ret) { case MBEDTLS_ERR_SSL_WANT_READ: WriteReturnValue(SSL_ERR_RAGAIN, BufferIn); if (!nonBlock) ReturnValue = SSL_ERR_RAGAIN; break; case MBEDTLS_ERR_SSL_WANT_WRITE: WriteReturnValue(SSL_ERR_WAGAIN, BufferIn); if (!nonBlock) ReturnValue = SSL_ERR_WAGAIN; break; default: WriteReturnValue(SSL_ERR_FAILED, BufferIn); break; } } break; } case IOCTLV_NET_SSL_READ: { int ret = mbedtls_ssl_read(&Device::NetSSL::_SSL[sslID].ctx, Memory::GetPointer(BufferIn2), BufferInSize2); if (SConfig::GetInstance().m_SSLDumpRead && ret > 0) { std::string filename = File::GetUserPath(D_DUMPSSL_IDX) + SConfig::GetInstance().GetGameID() + "_read.bin"; File::IOFile(filename, "ab").WriteBytes(Memory::GetPointer(BufferIn2), ret); } if (ret >= 0) { // Return bytes read or SSL_ERR_ZERO if none WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn); } else { switch (ret) { case MBEDTLS_ERR_SSL_WANT_READ: WriteReturnValue(SSL_ERR_RAGAIN, BufferIn); if (!nonBlock) ReturnValue = SSL_ERR_RAGAIN; break; case MBEDTLS_ERR_SSL_WANT_WRITE: WriteReturnValue(SSL_ERR_WAGAIN, BufferIn); if (!nonBlock) ReturnValue = SSL_ERR_WAGAIN; break; default: WriteReturnValue(SSL_ERR_FAILED, BufferIn); break; } } break; } default: break; } } else { WriteReturnValue(SSL_ERR_ID, BufferIn); } } else { switch (it->net_type) { case IOCTLV_SO_SENDTO: { u32 flags = Memory::Read_U32(BufferIn2 + 0x04); u32 has_destaddr = Memory::Read_U32(BufferIn2 + 0x08); // Not a string, Windows requires a const char* for sendto const char* data = (const char*)Memory::GetPointer(BufferIn); // Act as non blocking when SO_MSG_NONBLOCK is specified forceNonBlock = ((flags & SO_MSG_NONBLOCK) == SO_MSG_NONBLOCK); // send/sendto only handles MSG_OOB flags &= SO_MSG_OOB; sockaddr_in local_name = {0}; if (has_destaddr) { WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferIn2 + 0x0C); WiiSockMan::Convert(*wii_name, local_name); } int ret = sendto(fd, data, BufferInSize, flags, has_destaddr ? (struct sockaddr*)&local_name : nullptr, has_destaddr ? sizeof(sockaddr) : 0); ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_SENDTO", true); INFO_LOG(IOS_NET, "%s = %d Socket: %08x, BufferIn: (%08x, %i), BufferIn2: (%08x, %i), %u.%u.%u.%u", has_destaddr ? "IOCTLV_SO_SENDTO " : "IOCTLV_SO_SEND ", ReturnValue, wii_fd, BufferIn, BufferInSize, BufferIn2, BufferInSize2, local_name.sin_addr.s_addr & 0xFF, (local_name.sin_addr.s_addr >> 8) & 0xFF, (local_name.sin_addr.s_addr >> 16) & 0xFF, (local_name.sin_addr.s_addr >> 24) & 0xFF); break; } case IOCTLV_SO_RECVFROM: { u32 flags = Memory::Read_U32(BufferIn + 0x04); // Not a string, Windows requires a char* for recvfrom char* data = (char*)Memory::GetPointer(BufferOut); int data_len = BufferOutSize; sockaddr_in local_name; memset(&local_name, 0, sizeof(sockaddr_in)); if (BufferOutSize2 != 0) { WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut2); WiiSockMan::Convert(*wii_name, local_name); } // Act as non blocking when SO_MSG_NONBLOCK is specified forceNonBlock = ((flags & SO_MSG_NONBLOCK) == SO_MSG_NONBLOCK); // recv/recvfrom only handles PEEK/OOB flags &= SO_MSG_PEEK | SO_MSG_OOB; #ifdef _WIN32 if (flags & SO_MSG_PEEK) { unsigned long totallen = 0; ioctlsocket(fd, FIONREAD, &totallen); ReturnValue = totallen; break; } #endif socklen_t addrlen = sizeof(sockaddr_in); int ret = recvfrom(fd, data, data_len, flags, BufferOutSize2 ? (struct sockaddr*)&local_name : nullptr, BufferOutSize2 ? &addrlen : nullptr); ReturnValue = WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true); INFO_LOG(IOS_NET, "%s(%d, %p) Socket: %08X, Flags: %08X, " "BufferIn: (%08x, %i), BufferIn2: (%08x, %i), " "BufferOut: (%08x, %i), BufferOut2: (%08x, %i)", BufferOutSize2 ? "IOCTLV_SO_RECVFROM " : "IOCTLV_SO_RECV ", ReturnValue, data, wii_fd, flags, BufferIn, BufferInSize, BufferIn2, BufferInSize2, BufferOut, BufferOutSize, BufferOut2, BufferOutSize2); if (BufferOutSize2 != 0) { WiiSockAddrIn* wii_name = (WiiSockAddrIn*)Memory::GetPointer(BufferOut2); WiiSockMan::Convert(local_name, *wii_name, addrlen); } break; } default: break; } } } if (nonBlock || forceNonBlock || (!it->is_ssl && ReturnValue != -SO_EAGAIN && ReturnValue != -SO_EINPROGRESS && ReturnValue != -SO_EALREADY) || (it->is_ssl && ReturnValue != SSL_ERR_WAGAIN && ReturnValue != SSL_ERR_RAGAIN)) { DEBUG_LOG(IOS_NET, "IOCTL(V) Sock: %08x ioctl/v: %d returned: %d nonBlock: %d forceNonBlock: %d", wii_fd, it->is_ssl ? (int)it->ssl_type : (int)it->net_type, ReturnValue, nonBlock, forceNonBlock); // TODO: remove the dependency on a running IOS instance. GetIOS()->EnqueueIPCReply(it->request, ReturnValue); it = pending_sockops.erase(it); } else { ++it; } }