diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index 623ba202c562a..17c6d12131e4d 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -13,6 +13,7 @@ #include "Host.h" #include "R3000A.h" #include "Common.h" +#include "IopBios.h" #include "IopHw.h" #include "IopDma.h" #include "VMManager.h" @@ -416,10 +417,11 @@ static bool cdvdUncheckedLoadDiscElf(ElfObject* elfo, IsoReader& isor, const std bool cdvdLoadElf(ElfObject* elfo, const std::string_view& elfpath, bool isPSXElf, Error* error) { - if (elfpath.starts_with("host:")) + if (R3000A::ioman::is_host(elfpath)) { - std::string host_filename(elfpath.substr(5)); - return elfo->OpenFile(host_filename, isPSXElf, error); + const std::string_view path(elfpath.substr(elfpath.find(':') + 1)); + const std::string file_path(R3000A::ioman::host_path(path, false)); + return elfo->OpenFile(file_path, isPSXElf, error); } else if (elfpath.starts_with("cdrom:") || elfpath.starts_with("cdrom0:")) { diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 4ecc6b50b9d1c..572b94ab442c6 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -287,6 +287,7 @@ set(pcsx2DEV9Sources DEV9/ATA/ATA_State.cpp DEV9/ATA/ATA_Transfer.cpp DEV9/ATA/HddCreate.cpp + DEV9/InternalServers/DHCP_Logger.cpp DEV9/InternalServers/DHCP_Server.cpp DEV9/InternalServers/DNS_Logger.cpp DEV9/InternalServers/DNS_Server.cpp @@ -325,7 +326,8 @@ set(pcsx2DEV9Headers DEV9/ATA/ATA.h DEV9/ATA/HddCreate.h DEV9/DEV9.h - DEV9/InternalServers/DHCP_Server.cpp + DEV9/InternalServers/DHCP_Logger.h + DEV9/InternalServers/DHCP_Server.h DEV9/InternalServers/DNS_Logger.h DEV9/InternalServers/DNS_Server.h DEV9/net.h diff --git a/pcsx2/Config.h b/pcsx2/Config.h index e4ab52b9a4afb..c8e1a4a33346a 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -861,6 +861,7 @@ struct Pcsx2Config bool EthEnable{false}; NetApi EthApi{NetApi::Unset}; std::string EthDevice; + bool EthLogDHCP{false}; bool EthLogDNS{false}; bool InterceptDHCP{false}; diff --git a/pcsx2/DEV9/InternalServers/DHCP_Logger.cpp b/pcsx2/DEV9/InternalServers/DHCP_Logger.cpp new file mode 100644 index 0000000000000..f0ebd81bc75c2 --- /dev/null +++ b/pcsx2/DEV9/InternalServers/DHCP_Logger.cpp @@ -0,0 +1,338 @@ +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team +// SPDX-License-Identifier: LGPL-3.0+ + +#include "DHCP_Logger.h" +#include "DEV9/PacketReader/IP/UDP/UDP_Packet.h" +#include "DEV9/PacketReader/IP/UDP/DHCP/DHCP_Packet.h" + +#include + +#include "common/Console.h" +#include "common/StringUtil.h" + +#include "DEV9/AdapterUtils.h" + +using PacketReader::PayloadPtr; +using namespace PacketReader::IP; +using namespace PacketReader::IP::UDP; +using namespace PacketReader::IP::UDP::DHCP; + +namespace InternalServers +{ +#ifdef _WIN32 + void DHCP_Logger::Init(PIP_ADAPTER_ADDRESSES adapter) +#elif defined(__POSIX__) + void DHCP_Logger::Init(ifaddrs* adapter) +#endif + { + std::optional adIP = AdapterUtils::GetAdapterIP(adapter); + if (adIP.has_value()) + pcIP = adIP.value(); + else + pcIP = {}; + } + + void DHCP_Logger::InspectRecv(IP_Payload* payload) + { + UDP_Packet* udpPacket = static_cast(payload); + PayloadPtr* udpPayload = static_cast(udpPacket->GetPayload()); + DHCP_Packet dhcp(udpPayload->data, udpPayload->GetLength()); + Console.WriteLn("DEV9: DHCP: Host PC IP is %s", IpToString(pcIP).c_str()); + LogPacket(&dhcp); + } + + void DHCP_Logger::InspectSend(IP_Payload* payload) + { + UDP_Packet* udpPacket = static_cast(payload); + PayloadPtr* udpPayload = static_cast(udpPacket->GetPayload()); + DHCP_Packet dhcp(udpPayload->data, udpPayload->GetLength()); + Console.WriteLn("DEV9: DHCP: Host PC IP is %s", IpToString(pcIP).c_str()); + LogPacket(&dhcp); + } + + std::string DHCP_Logger::IpToString(IP_Address ip) + { + return StringUtil::StdStringFromFormat("%u.%u.%u.%u", ip.bytes[0], ip.bytes[1], ip.bytes[2], ip.bytes[3]); + } + + std::string DHCP_Logger::HardwareAddressToString(u8* data, size_t len) + { + std::string str; + if (len != 0) + { + str.reserve(len * 4); + for (size_t i = 0; i < len; i++) + str += StringUtil::StdStringFromFormat("%.2X:", data[i]); + + str.pop_back(); + } // else leave string empty + return str; + } + + std::string DHCP_Logger::ClientIdToString(const std::vector& data) + { + std::string str; + if (data.size() != 0) + { + str.reserve(data.size() * 4); + for (size_t i = 0; i < data.size(); i++) + str += StringUtil::StdStringFromFormat("%.2X:", data[i]); + + str.pop_back(); + } // else leave string empty + return str; + } + + const char* DHCP_Logger::OpToString(u8 op) + { + switch (op) + { + case 1: + return "Request"; + case 2: + return "Reply"; + default: + return "Unknown"; + } + } + + const char* DHCP_Logger::HardwareTypeToString(u8 op) + { + switch (op) + { + case 1: + return "Ethernet"; + case 6: + return "IEEE 802"; + default: + return "Unknown"; + } + } + + const char* DHCP_Logger::OptionToString(u8 option) + { + switch (option) + { + case 0: + return "Nop"; + case 1: + return "Subnet"; + case 3: + return "Routers"; + case 6: + return "DNS"; + case 12: + return "Host Name"; + case 15: + return "DNS Name"; + case 28: + return "Broadcast IP"; + case 46: + return "NetBIOS Type"; + case 50: + return "Requested IP"; + case 51: + return "IP Lease Time"; + case 53: + return "Message Type"; + case 54: + return "Server IP"; + case 55: + return "Request List"; + case 56: + return "Message String"; + case 57: + return "Max Message Size"; + case 58: + return "Renewal Time T1"; + case 59: + return "Rebinding Time T2"; + case 60: + return "Class ID"; + case 61: + return "Client ID"; + case 255: + return "End"; + default: + return "Unknown"; + } + } + + const char* DHCP_Logger::MessageCodeToString(u8 op) + { + switch (op) + { + case 1: + return "DHCP Discover"; + case 2: + return "DHCP Offer"; + case 3: + return "DHCP Request"; + case 4: + return "DHCP Decline"; + case 5: + return "DHCP ACK"; + case 6: + return "DHCP NACK"; + case 7: + return "DHCP Release"; + case 8: + return "DHCP Inform"; + default: + return "Unknown"; + } + } + + void DHCP_Logger::LogPacket(DHCP_Packet* dhcp) + { + Console.WriteLn("DEV9: DHCP: Op %s (%i)", OpToString(dhcp->op), dhcp->op); + Console.WriteLn("DEV9: DHCP: Hardware Type %s (%i)", HardwareTypeToString(dhcp->hardwareType), dhcp->hardwareType); + Console.WriteLn("DEV9: DHCP: Hardware Address Length %i", dhcp->hardwareAddressLength); + Console.WriteLn("DEV9: DHCP: Hops %i", dhcp->hops); + Console.WriteLn("DEV9: DHCP: Transaction ID %i", dhcp->transactionID); + Console.WriteLn("DEV9: DHCP: Seconds %i", dhcp->seconds); + Console.WriteLn("DEV9: DHCP: Flags 0x%.4X", dhcp->flags); + Console.WriteLn("DEV9: DHCP: Client IP %s", IpToString(dhcp->clientIP).c_str()); + Console.WriteLn("DEV9: DHCP: Your IP %s", IpToString(dhcp->yourIP).c_str()); + Console.WriteLn("DEV9: DHCP: Server IP %s", IpToString(dhcp->serverIP).c_str()); + Console.WriteLn("DEV9: DHCP: Gateway IP %s", IpToString(dhcp->gatewayIP).c_str()); + Console.WriteLn("DEV9: DHCP: Gateway IP %s", IpToString(dhcp->gatewayIP).c_str()); + Console.WriteLn("DEV9: DHCP: Client Hardware Address %s", HardwareAddressToString(dhcp->clientHardwareAddress, std::min(dhcp->hardwareAddressLength, 16)).c_str()); + Console.WriteLn("DEV9: DHCP: Magic Cookie 0x%.8X", dhcp->magicCookie); + + Console.WriteLn("DEV9: DHCP: Options Count %i", dhcp->options.size()); + + for (size_t i = 0; i < dhcp->options.size(); i++) + { + BaseOption* entry = dhcp->options[i]; + Console.WriteLn("DEV9: DHCP: Option %s (%i)", OptionToString(entry->GetCode()), entry->GetCode()); + Console.WriteLn("DEV9: DHCP: Option Size %i", entry->GetLength()); + switch (entry->GetCode()) + { + case 0: + break; + case 1: + { + const DHCPopSubnet* subnet = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Subnet %s", IpToString(subnet->subnetMask).c_str()); + break; + } + case 3: + { + const DHCPopRouter* routers = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Routers Count %i", routers->routers.size()); + for (size_t j = 0; j < routers->routers.size(); j++) + Console.WriteLn("DEV9: DHCP: Router %s", IpToString(routers->routers[j]).c_str()); + break; + } + case 6: + { + const DHCPopDNS* dns = static_cast(entry); + Console.WriteLn("DEV9: DHCP: DNS Count %i", dns->dnsServers.size()); + for (size_t j = 0; j < dns->dnsServers.size(); j++) + Console.WriteLn("DEV9: DHCP: DNS %s", IpToString(dns->dnsServers[j]).c_str()); + break; + } + case 12: + { + const DHCPopHostName* name = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Host Name %s", name->hostName.c_str()); + break; + } + case 15: + { + const DHCPopDnsName* name = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Domain Name %s", name->domainName.c_str()); + break; + } + case 28: + { + const DHCPopBCIP* broadcast = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Broadcast IP %s", IpToString(broadcast->broadcastIP).c_str()); + break; + } + case 46: + { + DHCPopNBIOSType* biosType = static_cast(entry); + Console.WriteLn("DEV9: DHCP: NetBIOS B-Node %s", biosType->GetBNode() ? "True" : "False"); + Console.WriteLn("DEV9: DHCP: NetBIOS P-Node %s", biosType->GetPNode() ? "True" : "False"); + Console.WriteLn("DEV9: DHCP: NetBIOS M-Node %s", biosType->GetMNode() ? "True" : "False"); + Console.WriteLn("DEV9: DHCP: NetBIOS H-Node %s", biosType->GetHNode() ? "True" : "False"); + break; + } + case 50: + { + const DHCPopREQIP* req = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Requested IP %s", IpToString(req->requestedIP).c_str()); + break; + } + case 51: + { + const DHCPopIPLT* iplt = static_cast(entry); + Console.WriteLn("DEV9: DHCP: IP Least Time %i", iplt->ipLeaseTime); + break; + } + case 53: + { + const DHCPopMSG* msg = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Message %s (%i)", MessageCodeToString(msg->message), msg->message); + break; + } + case 54: + { + const DHCPopSERVIP* req = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Server IP %s", IpToString(req->serverIP).c_str()); + break; + } + case 55: + { + const DHCPopREQLIST* reqList = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Request Count %i", reqList->requests.size()); + for (size_t j = 0; j < reqList->requests.size(); j++) + Console.WriteLn("DEV9: DHCP: Requested %s (%i)", OptionToString(reqList->requests[j]), reqList->requests[j]); + break; + } + case 56: + { + const DHCPopMSGStr* msg = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Message %s", msg->message.c_str()); + break; + } + case 57: + { + const DHCPopMMSGS* maxMs = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Max Message Size %i", maxMs->maxMessageSize); + break; + } + case 58: + { + const DHCPopT1* t1 = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Renewal Time (T1) %i", t1->ipRenewalTimeT1); + break; + } + case 59: + { + const DHCPopT2* t2 = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Rebinding Time (T2) %i", t2->ipRebindingTimeT2); + break; + } + case 60: + { + const DHCPopClassID* id = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Class ID %s", id->classID.c_str()); + break; + } + case 61: + { + const DHCPopClientID* id = static_cast(entry); + Console.WriteLn("DEV9: DHCP: Client ID %s", ClientIdToString(id->clientID).c_str()); + break; + } + case 255: + break; + default: + break; + } + } + } +} // namespace InternalServers diff --git a/pcsx2/DEV9/InternalServers/DHCP_Logger.h b/pcsx2/DEV9/InternalServers/DHCP_Logger.h new file mode 100644 index 0000000000000..5f796d5895434 --- /dev/null +++ b/pcsx2/DEV9/InternalServers/DHCP_Logger.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team +// SPDX-License-Identifier: LGPL-3.0+ + +#pragma once +#include "DEV9/PacketReader/IP/IP_Packet.h" +#include "DEV9/PacketReader/IP/UDP/DHCP/DHCP_Packet.h" + +#ifdef _WIN32 +#include +#include +#elif defined(__POSIX__) +#include +#include +#endif + +namespace InternalServers +{ + class DHCP_Logger + { + public: + DHCP_Logger(){}; + +#ifdef _WIN32 + void Init(PIP_ADAPTER_ADDRESSES adapter); +#elif defined(__POSIX__) + void Init(ifaddrs* adapter); +#endif + + // Expects a UDP_payload + void InspectRecv(PacketReader::IP::IP_Payload* payload); + // Expects a UDP_payload + void InspectSend(PacketReader::IP::IP_Payload* payload); + + private: + PacketReader::IP::IP_Address pcIP{}; + + std::string IpToString(PacketReader::IP::IP_Address ip); + std::string HardwareAddressToString(u8* data, size_t len); + std::string ClientIdToString(const std::vector& data); + const char* OpToString(u8 op); + const char* HardwareTypeToString(u8 op); + const char* OptionToString(u8 option); + const char* MessageCodeToString(u8 msg); + void LogPacket(PacketReader::IP::UDP::DHCP::DHCP_Packet* payload); + }; +} // namespace InternalServers diff --git a/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.cpp b/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.cpp index fcc1e429c43e2..ee48284d4689a 100644 --- a/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.cpp +++ b/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ +#include + #include "common/Assertions.h" #include "common/Console.h" @@ -28,12 +30,29 @@ using namespace PacketReader::IP::UDP; namespace Sessions { + /* + * The default UDP_Session backend don't bind to the src port the PS2 uses. + * Some games, however, sends the response to a set port, rather than the message source port + * A set of heuristics are used to determine when we should bind the port, these are; + * Any broadcast & multicast packet, and any packet where the src and dst ports are close to each other + * UDP_FixedPort manages the lifetime of socket bound to a specific port, and shares that socket + * with any UDP_Sessions created from it. + * For a UDP_Session with a parent UDP_FixedPort, packets are sent from the UDP_Session, but received + * by the UDP_FixedPort, with the UDP_FixedPort asking each UDP_Session associated with it whether + * it can accept the received packet, broadcast/multicast will accept eveything, while unicast sessions + * only accept packets from the address it sent to + */ + UDP_FixedPort::UDP_FixedPort(ConnectionKey parKey, IP_Address parAdapterIP, u16 parPort) : BaseSession(parKey, parAdapterIP) - , client{socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)} - , port(parPort) + , port{parPort} + { + } + + void UDP_FixedPort::Init() { int ret; + client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (client == INVALID_SOCKET) { Console.Error("DEV9: UDP: Failed to open socket. Error: %d", @@ -42,12 +61,12 @@ namespace Sessions #elif defined(__POSIX__) errno); #endif - //RaiseEventConnectionClosed(); //TODO + RaiseEventConnectionClosed(); return; } - const int reuseAddress = true; //BOOL - ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddress, sizeof(reuseAddress)); + const int reuseAddress = true; // BOOL on Windows + ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuseAddress), sizeof(reuseAddress)); if (ret == SOCKET_ERROR) Console.Error("DEV9: UDP: Failed to set SO_REUSEADDR. Error: %d", @@ -57,8 +76,8 @@ namespace Sessions errno); #endif - const int broadcastEnable = true; //BOOL - ret = setsockopt(client, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcastEnable, sizeof(broadcastEnable)); + const int broadcastEnable = true; // BOOL on Windows + ret = setsockopt(client, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&broadcastEnable), sizeof(broadcastEnable)); if (ret == SOCKET_ERROR) Console.Error("DEV9: UDP: Failed to set SO_BROADCAST. Error: %d", @@ -68,20 +87,26 @@ namespace Sessions errno); #endif - sockaddr_in endpoint{0}; + sockaddr_in endpoint{}; endpoint.sin_family = AF_INET; - *(IP_Address*)&endpoint.sin_addr = adapterIP; - endpoint.sin_port = htons(parPort); + endpoint.sin_addr = std::bit_cast(adapterIP); + endpoint.sin_port = htons(port); - ret = bind(client, (const sockaddr*)&endpoint, sizeof(endpoint)); + ret = bind(client, reinterpret_cast(&endpoint), sizeof(endpoint)); if (ret == SOCKET_ERROR) + { Console.Error("DEV9: UDP: Failed to bind socket. Error: %d", #ifdef _WIN32 WSAGetLastError()); #elif defined(__POSIX__) errno); #endif + RaiseEventConnectionClosed(); + return; + } + + open.store(true); } IP_Payload* UDP_FixedPort::Recv() @@ -93,7 +118,7 @@ namespace Sessions fd_set sReady; fd_set sExcept; - timeval nowait{0}; + timeval nowait{}; FD_ZERO(&sReady); FD_ZERO(&sExcept); FD_SET(client, &sReady); @@ -104,7 +129,7 @@ namespace Sessions if (ret == SOCKET_ERROR) { hasData = false; - Console.Error("DEV9: UDP: select failed. Error Code: %d", + Console.Error("DEV9: UDP: select failed. Error code: %d", #ifdef _WIN32 WSAGetLastError()); #elif defined(__POSIX__) @@ -118,15 +143,15 @@ namespace Sessions int error = 0; #ifdef _WIN32 int len = sizeof(error); - if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0) - Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", WSAGetLastError()); + if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len) < 0) + Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", WSAGetLastError()); #elif defined(__POSIX__) socklen_t len = sizeof(error); - if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0) - Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", errno); + if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len) < 0) + Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", errno); #endif else - Console.Error("DEV9: UDP: Recv Error: %d", error); + Console.Error("DEV9: UDP: Recv error: %d", error); } else hasData = FD_ISSET(client, &sReady); @@ -136,10 +161,10 @@ namespace Sessions unsigned long available = 0; PayloadData* recived = nullptr; std::unique_ptr buffer; - sockaddr endpoint{0}; + sockaddr_in endpoint{}; - //FIONREAD returns total size of all available messages - //but we will read one message at a time + // FIONREAD returns total size of all available messages + // however, we only read one message at a time #ifdef _WIN32 ret = ioctlsocket(client, FIONREAD, &available); #elif defined(__POSIX__) @@ -154,12 +179,12 @@ namespace Sessions #elif defined(__POSIX__) socklen_t fromlen = sizeof(endpoint); #endif - ret = recvfrom(client, (char*)buffer.get(), available, 0, &endpoint, &fromlen); + ret = recvfrom(client, reinterpret_cast(buffer.get()), available, 0, reinterpret_cast(&endpoint), &fromlen); } if (ret == SOCKET_ERROR) { - Console.Error("UDP Recv Error: %d", + Console.Error("DEV9: UDP: UDP recv error: %d", #ifdef _WIN32 WSAGetLastError()); #elif defined(__POSIX__) @@ -175,9 +200,8 @@ namespace Sessions UDP_Packet* iRet = new UDP_Packet(recived); iRet->destinationPort = port; - sockaddr_in* sockaddr = (sockaddr_in*)&endpoint; - destIP = *(IP_Address*)&sockaddr->sin_addr; - iRet->sourcePort = ntohs(sockaddr->sin_port); + destIP = std::bit_cast(endpoint.sin_addr); + iRet->sourcePort = ntohs(endpoint.sin_port); { std::lock_guard numberlock(connectionSentry); @@ -210,10 +234,12 @@ namespace Sessions UDP_Session* UDP_FixedPort::NewClientSession(ConnectionKey parNewKey, bool parIsBrodcast, bool parIsMulticast) { + if (!open.load()) + return nullptr; + UDP_Session* s = new UDP_Session(parNewKey, adapterIP, parIsBrodcast, parIsMulticast, client); s->AddConnectionClosedHandler([&](BaseSession* session) { HandleChildConnectionClosed(session); }); - { std::lock_guard numberlock(connectionSentry); connections.push_back(s); @@ -230,7 +256,10 @@ namespace Sessions { connections.erase(index); if (connections.size() == 0) + { + open.store(false); RaiseEventConnectionClosed(); + } } } diff --git a/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.h b/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.h index d66eaf9140010..58b46e9240047 100644 --- a/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.h +++ b/pcsx2/DEV9/Sessions/UDP_Session/UDP_FixedPort.h @@ -20,7 +20,7 @@ namespace Sessions class UDP_FixedPort : public BaseSession { private: - std::atomic open{true}; + std::atomic open{false}; #ifdef _WIN32 SOCKET client = INVALID_SOCKET; @@ -38,6 +38,8 @@ namespace Sessions public: UDP_FixedPort(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP, u16 parPort); + void Init(); + virtual PacketReader::IP::IP_Payload* Recv(); virtual bool Send(PacketReader::IP::IP_Payload* payload); virtual void Reset(); diff --git a/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.cpp b/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.cpp index fe710410977b7..007e3c92f9154 100644 --- a/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.cpp +++ b/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.cpp @@ -31,13 +31,12 @@ using namespace std::chrono_literals; namespace Sessions { const std::chrono::duration - UDP_Session::MAX_IDLE = 120s; //See RFC 4787 Section 4.3 - - //TODO, figure out handling of multicast + UDP_Session::MAX_IDLE = 120s; // See RFC 4787 section 4.3 UDP_Session::UDP_Session(ConnectionKey parKey, IP_Address parAdapterIP) : UDP_BaseSession(parKey, parAdapterIP) , isBroadcast(false) + , isMulticast(false) , isFixedPort(false) , deathClockStart(std::chrono::steady_clock::now()) { @@ -62,15 +61,15 @@ namespace Sessions IP_Payload* UDP_Session::Recv() { - if (!open) + if (!open.load()) return nullptr; if (isFixedPort) { if (std::chrono::steady_clock::now() - deathClockStart.load() > MAX_IDLE) { - CloseSocket(); - Console.WriteLn("DEV9: UDP: UDPFixed Max Idle Reached"); + Console.WriteLn("DEV9: UDP: Fixed port max idle reached"); + open.store(false); RaiseEventConnectionClosed(); } return nullptr; @@ -80,7 +79,7 @@ namespace Sessions fd_set sReady; fd_set sExcept; - timeval nowait{0}; + timeval nowait{}; FD_ZERO(&sReady); FD_ZERO(&sExcept); FD_SET(client, &sReady); @@ -91,7 +90,7 @@ namespace Sessions if (ret == SOCKET_ERROR) { hasData = false; - Console.Error("DEV9: UDP: Select Failed. Error Code: %d", + Console.Error("DEV9: UDP: Select failed. Error code: %d", #ifdef _WIN32 WSAGetLastError()); #elif defined(__POSIX__) @@ -105,15 +104,15 @@ namespace Sessions int error = 0; #ifdef _WIN32 int len = sizeof(error); - if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0) - Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", WSAGetLastError()); + if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len) < 0) + Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", WSAGetLastError()); #elif defined(__POSIX__) socklen_t len = sizeof(error); - if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0) - Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", errno); + if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len) < 0) + Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", errno); #endif else - Console.Error("DEV9: UDP: Recv Error: %d", error); + Console.Error("DEV9: UDP: Recv error: %d", error); } else hasData = FD_ISSET(client, &sReady); @@ -123,10 +122,10 @@ namespace Sessions unsigned long available = 0; PayloadData* recived = nullptr; std::unique_ptr buffer; - sockaddr endpoint{0}; + sockaddr_in endpoint{}; - //FIONREAD returns total size of all available messages - //but we will read one message at a time + // FIONREAD returns total size of all available messages + // however, we only read one message at a time #ifdef _WIN32 ret = ioctlsocket(client, FIONREAD, &available); #elif defined(__POSIX__) @@ -141,12 +140,12 @@ namespace Sessions #elif defined(__POSIX__) socklen_t fromlen = sizeof(endpoint); #endif - ret = recvfrom(client, (char*)buffer.get(), available, 0, &endpoint, &fromlen); + ret = recvfrom(client, reinterpret_cast(buffer.get()), available, 0, reinterpret_cast(&endpoint), &fromlen); } if (ret == SOCKET_ERROR) { - Console.Error("DEV9: UDP: Recv Error: %d", + Console.Error("DEV9: UDP: Recv error: %d", #ifdef _WIN32 WSAGetLastError()); #elif defined(__POSIX__) @@ -170,8 +169,7 @@ namespace Sessions if (std::chrono::steady_clock::now() - deathClockStart.load() > MAX_IDLE) { - //CloseSocket(); - Console.WriteLn("DEV9: UDP: Max Idle Reached"); + Console.WriteLn("DEV9: UDP: Max idle reached"); RaiseEventConnectionClosed(); } @@ -180,10 +178,10 @@ namespace Sessions bool UDP_Session::WillRecive(IP_Address parDestIP) { - if (!open) + if (!open.load()) return false; - if (isBroadcast || (parDestIP == destIP)) + if (isBroadcast || isMulticast || (parDestIP == destIP)) { deathClockStart.store(std::chrono::steady_clock::now()); return true; @@ -200,26 +198,19 @@ namespace Sessions if (destPort != 0) { - //client already created + // Already created client!? if (!(udp.destinationPort == destPort && udp.sourcePort == srcPort)) { - Console.Error("DEV9: UDP: Packet invalid for current session (Duplicate key?)"); + Console.Error("DEV9: UDP: Packet invalid for current session (duplicate key?)"); return false; } } else { - //create client + // Create client destPort = udp.destinationPort; srcPort = udp.sourcePort; - //Multicast address start with 0b1110 - if ((destIP.bytes[0] & 0xF0) == 0xE0) - { - isMulticast = true; - Console.Error("DEV9: UDP: Unexpected Multicast Connection"); - } - int ret; client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (client == INVALID_SOCKET) @@ -234,8 +225,8 @@ namespace Sessions return false; } - const int reuseAddress = true; //BOOL - ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddress, sizeof(reuseAddress)); + const int reuseAddress = true; // BOOL on Windows + ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuseAddress), sizeof(reuseAddress)); if (ret == SOCKET_ERROR) Console.Error("DEV9: UDP: Failed to set SO_REUSEADDR. Error: %d", @@ -247,11 +238,11 @@ namespace Sessions if (adapterIP.integer != 0) { - sockaddr_in endpoint{0}; + sockaddr_in endpoint{}; endpoint.sin_family = AF_INET; - *(IP_Address*)&endpoint.sin_addr = adapterIP; + endpoint.sin_addr = std::bit_cast(adapterIP); - ret = bind(client, (const sockaddr*)&endpoint, sizeof(endpoint)); + ret = bind(client, reinterpret_cast(&endpoint), sizeof(endpoint)); if (ret == SOCKET_ERROR) Console.Error("DEV9: UDP: Failed to bind socket. Error: %d", @@ -262,14 +253,12 @@ namespace Sessions #endif } - pxAssert(isMulticast == false); - - sockaddr_in endpoint{0}; + sockaddr_in endpoint{}; endpoint.sin_family = AF_INET; - *(IP_Address*)&endpoint.sin_addr = destIP; + endpoint.sin_addr = std::bit_cast(destIP); endpoint.sin_port = htons(destPort); - ret = connect(client, (const sockaddr*)&endpoint, sizeof(endpoint)); + ret = connect(client, reinterpret_cast(&endpoint), sizeof(endpoint)); if (ret == SOCKET_ERROR) { @@ -284,33 +273,33 @@ namespace Sessions } if (srcPort != 0) - open = true; + open.store(true); } PayloadPtr* udpPayload = static_cast(udp.GetPayload()); - //Send + // Send Packet int ret = SOCKET_ERROR; if (isBroadcast) { - sockaddr_in endpoint{0}; + sockaddr_in endpoint{}; endpoint.sin_family = AF_INET; endpoint.sin_addr.s_addr = INADDR_BROADCAST; endpoint.sin_port = htons(destPort); - ret = sendto(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0, (const sockaddr*)&endpoint, sizeof(endpoint)); + ret = sendto(client, reinterpret_cast(udpPayload->data), udpPayload->GetLength(), 0, reinterpret_cast(&endpoint), sizeof(endpoint)); } - else if (isMulticast | isFixedPort) + else if (isFixedPort) { - sockaddr_in endpoint{0}; + sockaddr_in endpoint{}; endpoint.sin_family = AF_INET; - *(IP_Address*)&endpoint.sin_addr = destIP; + endpoint.sin_addr = std::bit_cast(destIP); endpoint.sin_port = htons(destPort); - ret = sendto(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0, (const sockaddr*)&endpoint, sizeof(endpoint)); + ret = sendto(client, reinterpret_cast(udpPayload->data), udpPayload->GetLength(), 0, reinterpret_cast(&endpoint), sizeof(endpoint)); } else - ret = send(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0); + ret = send(client, reinterpret_cast(udpPayload->data), udpPayload->GetLength(), 0); if (ret == SOCKET_ERROR) { @@ -319,13 +308,14 @@ namespace Sessions #elif defined(__POSIX__) ret = errno; #endif - Console.Error("DEV9: UDP: Send Error %d", ret); - - //We can recive an ICMP Port Unreacable error, which can get raised in send (and maybe sendto?) - //On Windows this an WSAECONNRESET error, although I've not been able to reproduce in testing - //On Linux this is an ECONNREFUSED error (Testing needed to confirm full behaviour) - - //The decision to ignore the error and retry was made to allow R&C Deadlock ressurection team to packet capture eveything + Console.Error("DEV9: UDP: Send error %d", ret); + + /* + * We can receive an ICMP Port Unreacable error, which can get raised in send (and maybe sendto?) + * On Windows this is an WSAECONNRESET error, although I've not been able to reproduce in testing + * On Linux this is an ECONNREFUSED error (Testing needed to confirm full behaviour) + * We ignore the error and resend to allow packet capture (i.e. wireshark) for server resurrection projects + */ #ifdef _WIN32 if (ret == WSAECONNRESET) #elif defined(__POSIX__) @@ -335,20 +325,19 @@ namespace Sessions pxAssert(isBroadcast == false && isMulticast == false); if (isFixedPort) { - sockaddr_in endpoint{0}; + sockaddr_in endpoint{}; endpoint.sin_family = AF_INET; - *(IP_Address*)&endpoint.sin_addr = destIP; + endpoint.sin_addr = std::bit_cast(destIP); endpoint.sin_port = htons(destPort); - ret = sendto(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0, (const sockaddr*)&endpoint, sizeof(endpoint)); + ret = sendto(client, reinterpret_cast(udpPayload->data), udpPayload->GetLength(), 0, reinterpret_cast(&endpoint), sizeof(endpoint)); } else - //Do we need to clear the error somehow? - ret = send(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0); + ret = send(client, reinterpret_cast(udpPayload->data), udpPayload->GetLength(), 0); if (ret == SOCKET_ERROR) { - Console.Error("DEV9: UDP: Send Error (Second attempt) %d", + Console.Error("DEV9: UDP: Send error (second attempt) %d", #ifdef _WIN32 WSAGetLastError()); #elif defined(__POSIX__) @@ -371,9 +360,14 @@ namespace Sessions return true; } - void UDP_Session::CloseSocket() + void UDP_Session::Reset() + { + RaiseEventConnectionClosed(); + } + + UDP_Session::~UDP_Session() { - open = false; + open.store(false); if (!isFixedPort && client != INVALID_SOCKET) { #ifdef _WIN32 @@ -384,15 +378,4 @@ namespace Sessions client = INVALID_SOCKET; } } - - void UDP_Session::Reset() - { - //CloseSocket(); - RaiseEventConnectionClosed(); - } - - UDP_Session::~UDP_Session() - { - CloseSocket(); - } } // namespace Sessions diff --git a/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.h b/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.h index d25d6839aeb21..9efd11644f117 100644 --- a/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.h +++ b/pcsx2/DEV9/Sessions/UDP_Session/UDP_Session.h @@ -28,19 +28,18 @@ namespace Sessions u16 srcPort = 0; u16 destPort = 0; - //Broadcast - const bool isBroadcast; // = false; - bool isMulticast = false; - const bool isFixedPort; // = false; - //EndBroadcast + // UDP_Session flags + const bool isBroadcast; + const bool isMulticast; + const bool isFixedPort; std::atomic deathClockStart; const static std::chrono::duration MAX_IDLE; public: - //Normal Port + // Normal Port UDP_Session(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP); - //Fixed Port + // Fixed Port #ifdef _WIN32 UDP_Session(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP, bool parIsBroadcast, bool parIsMulticast, SOCKET parClient); #elif defined(__POSIX__) @@ -53,8 +52,5 @@ namespace Sessions virtual void Reset(); virtual ~UDP_Session(); - - private: - void CloseSocket(); }; } // namespace Sessions diff --git a/pcsx2/DEV9/net.cpp b/pcsx2/DEV9/net.cpp index ffcd741a4703f..5c10891a07d4e 100644 --- a/pcsx2/DEV9/net.cpp +++ b/pcsx2/DEV9/net.cpp @@ -205,7 +205,7 @@ NetAdapter::~NetAdapter() void NetAdapter::InspectSend(NetPacket* pkt) { - if (EmuConfig.DEV9.EthLogDNS) + if (EmuConfig.DEV9.EthLogDNS || EmuConfig.DEV9.EthLogDHCP) { EthernetFrame frame(pkt); if (frame.protocol == (u16)EtherType::IPv4) @@ -218,19 +218,26 @@ void NetAdapter::InspectSend(NetPacket* pkt) IP_PayloadPtr* ipPayload = static_cast(ippkt.GetPayload()); UDP_Packet udppkt(ipPayload->data, ipPayload->GetLength()); - if (udppkt.destinationPort == 53) + if (EmuConfig.DEV9.EthLogDNS && udppkt.destinationPort == 53) { Console.WriteLn("DEV9: DNS: Packet Sent To %i.%i.%i.%i", ippkt.destinationIP.bytes[0], ippkt.destinationIP.bytes[1], ippkt.destinationIP.bytes[2], ippkt.destinationIP.bytes[3]); dnsLogger.InspectSend(&udppkt); } + + if (EmuConfig.DEV9.EthLogDHCP && udppkt.destinationPort == 67) + { + Console.WriteLn("DEV9: DHCP: Packet Sent To %i.%i.%i.%i", + ippkt.destinationIP.bytes[0], ippkt.destinationIP.bytes[1], ippkt.destinationIP.bytes[2], ippkt.destinationIP.bytes[3]); + dhcpLogger.InspectSend(&udppkt); + } } } } } void NetAdapter::InspectRecv(NetPacket* pkt) { - if (EmuConfig.DEV9.EthLogDNS) + if (EmuConfig.DEV9.EthLogDNS || EmuConfig.DEV9.EthLogDHCP) { EthernetFrame frame(pkt); if (frame.protocol == (u16)EtherType::IPv4) @@ -243,12 +250,19 @@ void NetAdapter::InspectRecv(NetPacket* pkt) IP_PayloadPtr* ipPayload = static_cast(ippkt.GetPayload()); UDP_Packet udppkt(ipPayload->data, ipPayload->GetLength()); - if (udppkt.sourcePort == 53) + if (EmuConfig.DEV9.EthLogDNS && udppkt.sourcePort == 53) { Console.WriteLn("DEV9: DNS: Packet Sent From %i.%i.%i.%i", ippkt.sourceIP.bytes[0], ippkt.sourceIP.bytes[1], ippkt.sourceIP.bytes[2], ippkt.sourceIP.bytes[3]); dnsLogger.InspectRecv(&udppkt); } + + if (EmuConfig.DEV9.EthLogDHCP && udppkt.sourcePort == 67) + { + Console.WriteLn("DEV9: DHCP: Packet Sent From %i.%i.%i.%i", + ippkt.sourceIP.bytes[0], ippkt.sourceIP.bytes[1], ippkt.sourceIP.bytes[2], ippkt.sourceIP.bytes[3]); + dhcpLogger.InspectRecv(&udppkt); + } } } } @@ -293,6 +307,8 @@ void NetAdapter::InitInternalServer(ifaddrs* adapter, bool dhcpForceEnable, IP_A if (adapter == nullptr) Console.Error("DEV9: InitInternalServer() got nullptr for adapter"); + dhcpLogger.Init(adapter); + dhcpOn = EmuConfig.DEV9.InterceptDHCP || dhcpForceEnable; if (dhcpOn) dhcpServer.Init(adapter, ipOverride, subnetOverride, gatewayOvveride); @@ -336,6 +352,7 @@ bool NetAdapter::InternalServerRecv(NetPacket* pkt) frame.destinationMAC = ps2MAC; frame.protocol = (u16)EtherType::IPv4; frame.WritePacket(pkt); + InspectRecv(pkt); return true; } diff --git a/pcsx2/DEV9/net.h b/pcsx2/DEV9/net.h index 6c0dc9e0b407a..e78bc367e3fce 100644 --- a/pcsx2/DEV9/net.h +++ b/pcsx2/DEV9/net.h @@ -24,6 +24,7 @@ #include "PacketReader/MAC_Address.h" #include "PacketReader/IP/IP_Address.h" +#include "InternalServers/DHCP_Logger.h" #include "InternalServers/DHCP_Server.h" #include "InternalServers/DNS_Logger.h" #include "InternalServers/DNS_Server.h" @@ -99,6 +100,7 @@ class NetAdapter bool dhcpOn = false; protected: + InternalServers::DHCP_Logger dhcpLogger; InternalServers::DHCP_Server dhcpServer = InternalServers::DHCP_Server([&] { InternalSignalReceived(); }); InternalServers::DNS_Logger dnsLogger; InternalServers::DNS_Server dnsServer = InternalServers::DNS_Server([&] { InternalSignalReceived(); }); diff --git a/pcsx2/DEV9/sockets.cpp b/pcsx2/DEV9/sockets.cpp index 4c1aae975db33..4243118774167 100644 --- a/pcsx2/DEV9/sockets.cpp +++ b/pcsx2/DEV9/sockets.cpp @@ -531,12 +531,20 @@ bool SocketAdapter::SendUDP(ConnectionKey Key, IP_Packet* ipPkt) connections.Add(fKey, fPort); fixedUDPPorts.Add(udp.sourcePort, fPort); + + fPort->Init(); } Console.WriteLn("DEV9: Socket: Creating New UDP Connection from FixedPort %d to %d", udp.sourcePort, udp.destinationPort); s = fPort->NewClientSession(Key, ipPkt->destinationIP == dhcpServer.broadcastIP || ipPkt->destinationIP == IP_Address{{{255, 255, 255, 255}}}, (ipPkt->destinationIP.bytes[0] & 0xF0) == 0xE0); + + if (s == nullptr) + { + Console.Error("DEV9: Socket: Failed to Create New UDP Connection from FixedPort"); + return false; + } } else { diff --git a/pcsx2/IopBios.cpp b/pcsx2/IopBios.cpp index 5322461711e14..192158c86286d 100644 --- a/pcsx2/IopBios.cpp +++ b/pcsx2/IopBios.cpp @@ -149,51 +149,6 @@ namespace R3000A 0x1000, }; - static std::string host_path(const std::string& path, bool allow_open_host_root) - { - // We are NOT allowing to use the root of the host unit. - // For now it just supports relative folders from the location of the elf - std::string native_path(Path::Canonicalize(path)); - std::string new_path; - if (!hostRoot.empty() && native_path.starts_with(hostRoot)) - new_path = std::move(native_path); - else if (!hostRoot.empty()) // relative paths - new_path = Path::Combine(hostRoot, native_path); - - // Allow opening the ELF override. - if (new_path == VMManager::Internal::GetELFOverride()) - return new_path; - - // Allow nothing if hostfs isn't enabled. - if (!EmuConfig.HostFs) - { - new_path.clear(); - return new_path; - } - - // Double-check that it falls within the directory of the elf. - // Not a real sandbox, but emulators shouldn't be treated as such. Don't run untrusted code! - std::string canonicalized_path(Path::Canonicalize(new_path)); - - // Are we opening the root of host? (i.e. `host:.` or `host:`) - // We want to allow this as a directory open, but not as a file open. - if (!allow_open_host_root || canonicalized_path != hostRoot) - { - // Only allow descendants of the hostfs directory. - if (canonicalized_path.length() <= hostRoot.length() || // Length has to be equal or longer, - !canonicalized_path.starts_with(hostRoot) || // and start with the host root, - canonicalized_path[hostRoot.length()] != FS_OSPATH_SEPARATOR_CHARACTER) // and we can't access a sibling. - { - Console.Error(fmt::format( - "IopHLE: Denying access to path outside of ELF directory. Requested path: '{}', Resolved path: '{}', ELF directory: '{}'", - path, new_path, hostRoot)); - new_path.clear(); - } - } - - return new_path; - } - // This is a workaround for GHS on *NIX platforms // Whenever a program splits directories with a backslash (ulaunchelf) // the directory is considered non-existant @@ -207,7 +162,7 @@ namespace R3000A static int host_stat(const std::string path, fio_stat_t* host_stats, fio_stat_flags& stat = ioman_stat) { struct stat file_stats; - const std::string file_path(host_path(path, true)); + const std::string file_path(ioman::host_path(path, true)); if (!FileSystem::StatFile(file_path.c_str(), &file_stats)) return -IOP_ENOENT; @@ -303,7 +258,7 @@ namespace R3000A static int open(IOManFile** file, const std::string& full_path, s32 flags, u16 mode) { const std::string path(full_path.substr(full_path.find(':') + 1)); - const std::string file_path(host_path(path, false)); + const std::string file_path(ioman::host_path(path, false)); int native_flags = O_BINARY; // necessary in Windows. switch (flags & IOP_O_RDWR) @@ -401,7 +356,7 @@ namespace R3000A static int open(IOManDir** dir, const std::string& full_path) { std::string relativePath = full_path.substr(full_path.find(':') + 1); - std::string path = host_path(relativePath, true); + std::string path = ioman::host_path(relativePath, true); if (!FileSystem::DirectoryExists(path.c_str())) return -IOP_ENOENT; // Should return ENOTDIR if path is a file? @@ -425,13 +380,13 @@ namespace R3000A { fxio_dirent_t* hostcontent = (fxio_dirent_t*)buf; StringUtil::Strlcpy(hostcontent->name, dir->FileName, sizeof(hostcontent->name)); - host_stat(host_path(Path::Combine(basedir, dir->FileName), true), &hostcontent->stat); + host_stat(ioman::host_path(Path::Combine(basedir, dir->FileName), true), &hostcontent->stat); } else { fio_dirent_t* hostcontent = (fio_dirent_t*)buf; StringUtil::Strlcpy(hostcontent->name, dir->FileName, sizeof(hostcontent->name)); - host_stat(host_path(Path::Combine(basedir, dir->FileName), true), &hostcontent->stat); + host_stat(ioman::host_path(Path::Combine(basedir, dir->FileName), true), &hostcontent->stat); } dir = std::next(dir); @@ -559,7 +514,7 @@ namespace R3000A } } - bool is_host(const std::string path) + bool is_host(const std::string_view path) { auto not_number_pos = path.find_first_not_of("0123456789", 4); if (not_number_pos == std::string::npos) @@ -568,6 +523,51 @@ namespace R3000A return (path.compare(0, 4, "host") == 0 && path[not_number_pos] == ':'); } + std::string host_path(const std::string_view path, bool allow_open_host_root) + { + // We are NOT allowing to use the root of the host unit. + // For now it just supports relative folders from the location of the elf + std::string native_path(Path::Canonicalize(path)); + std::string new_path; + if (!hostRoot.empty() && native_path.starts_with(hostRoot)) + new_path = std::move(native_path); + else if (!hostRoot.empty()) // relative paths + new_path = Path::Combine(hostRoot, native_path); + + // Allow opening the ELF override. + if (new_path == VMManager::Internal::GetELFOverride()) + return new_path; + + // Allow nothing if hostfs isn't enabled. + if (!EmuConfig.HostFs) + { + new_path.clear(); + return new_path; + } + + // Double-check that it falls within the directory of the elf. + // Not a real sandbox, but emulators shouldn't be treated as such. Don't run untrusted code! + std::string canonicalized_path(Path::Canonicalize(new_path)); + + // Are we opening the root of host? (i.e. `host:.` or `host:`) + // We want to allow this as a directory open, but not as a file open. + if (!allow_open_host_root || canonicalized_path != hostRoot) + { + // Only allow descendants of the hostfs directory. + if (canonicalized_path.length() <= hostRoot.length() || // Length has to be equal or longer, + !canonicalized_path.starts_with(hostRoot) || // and start with the host root, + canonicalized_path[hostRoot.length()] != FS_OSPATH_SEPARATOR_CHARACTER) // and we can't access a sibling. + { + Console.Error(fmt::format( + "IopHLE: Denying access to path outside of ELF directory. Requested path: '{}', Resolved path: '{}', ELF directory: '{}'", + path, new_path, hostRoot)); + new_path.clear(); + } + } + + return new_path; + } + int open_HLE() { IOManFile* file = NULL; diff --git a/pcsx2/IopBios.h b/pcsx2/IopBios.h index 4a263b8943596..112b0cf390a63 100644 --- a/pcsx2/IopBios.h +++ b/pcsx2/IopBios.h @@ -71,6 +71,8 @@ namespace R3000A namespace ioman { void reset(); + bool is_host(const std::string_view path); + std::string host_path(const std::string_view path, bool allow_open_host_root); } } // namespace R3000A diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 3842b5bf01e6a..0c0b4428e0bda 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1162,6 +1162,7 @@ void Pcsx2Config::DEV9Options::LoadSave(SettingsWrapper& wrap) SettingsWrapEntry(EthEnable); SettingsWrapEnumEx(EthApi, "EthApi", NetApiNames); SettingsWrapEntry(EthDevice); + SettingsWrapEntry(EthLogDHCP); SettingsWrapEntry(EthLogDNS); SettingsWrapEntry(InterceptDHCP); @@ -1255,6 +1256,7 @@ bool Pcsx2Config::DEV9Options::operator==(const DEV9Options& right) const return OpEqu(EthEnable) && OpEqu(EthApi) && OpEqu(EthDevice) && + OpEqu(EthLogDHCP) && OpEqu(EthLogDNS) && OpEqu(InterceptDHCP) && diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index 584fb19b6578e..7661da8d4146a 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -152,6 +152,7 @@ + @@ -490,6 +491,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index a380ff0dfdbb0..deec19ba763f9 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -884,6 +884,9 @@ System\Ps2\DEV9 + + System\Ps2\DEV9\InternalServers + System\Ps2\DEV9\InternalServers @@ -1736,6 +1739,9 @@ System\Ps2\DEV9 + + System\Ps2\DEV9\InternalServers + System\Ps2\DEV9\InternalServers