diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index 48ce271ebf..1d6ec5ae3d 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -1272,7 +1272,7 @@ error_code sceNpBasicAddFriend(vm::cptr contact, vm::cptr body, s error_code sceNpBasicGetFriendListEntryCount(vm::ptr count) { - sceNp.warning("sceNpBasicGetFriendListEntryCount(count=*0x%x)", count); + sceNp.trace("sceNpBasicGetFriendListEntryCount(count=*0x%x)", count); auto& nph = g_fxo->get>(); @@ -1299,7 +1299,7 @@ error_code sceNpBasicGetFriendListEntryCount(vm::ptr count) error_code sceNpBasicGetFriendListEntry(u32 index, vm::ptr npid) { - sceNp.warning("sceNpBasicGetFriendListEntry(index=%d, npid=*0x%x)", index, npid); + sceNp.trace("sceNpBasicGetFriendListEntry(index=%d, npid=*0x%x)", index, npid); auto& nph = g_fxo->get>(); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h index 243d2367f3..3aa2d401b6 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h @@ -48,11 +48,17 @@ public: sys_net_linger linger; }; + struct sockopt_cache + { + sockopt_data data{}; + s32 len = 0; + }; + public: SAVESTATE_INIT_POS(7); // Dependency on RPCN lv2_socket(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); - lv2_socket(utils::serial&){} + lv2_socket(utils::serial&) {} lv2_socket(utils::serial&, lv2_socket_type type); static std::shared_ptr load(utils::serial& ar); void save(utils::serial&, bool save_only_this_class = false); @@ -94,7 +100,7 @@ public: virtual std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) = 0; virtual std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) = 0; - virtual std::optional sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) = 0; + virtual std::optional sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) = 0; virtual void close() = 0; virtual s32 shutdown(s32 how) = 0; @@ -114,7 +120,7 @@ protected: lv2_socket(utils::serial&, bool); shared_mutex mutex; - u32 lv2_id = 0; + s32 lv2_id = 0; socket_type socket = 0; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp index d6fdd671bd..e069c34d61 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp @@ -107,6 +107,9 @@ std::tuple, sys_net_sockaddr> lv2_socket_ auto newsock = std::make_shared(family, type, protocol); newsock->set_socket(native_socket, family, type, protocol); + // Sockets inherit non blocking behaviour from their parent + newsock->so_nbio = so_nbio; + sys_net_sockaddr ps3_addr = native_addr_to_sys_net_addr(native_addr); return {true, 0, std::move(newsock), ps3_addr}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp index f34eed38a6..59c66e311a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp @@ -14,7 +14,7 @@ lv2_socket_p2p::lv2_socket_p2p(lv2_socket_family family, lv2_socket_type type, l lv2_socket_p2p::lv2_socket_p2p(utils::serial& ar, lv2_socket_type type) : lv2_socket(ar, type) { - ar(port, vport); + ar(port, vport, bound_addr); std::deque>> data_dequeue{ar}; @@ -29,7 +29,7 @@ lv2_socket_p2p::lv2_socket_p2p(utils::serial& ar, lv2_socket_type type) void lv2_socket_p2p::save(utils::serial& ar) { static_cast(this)->save(ar, true); - ar(port, vport); + ar(port, vport, bound_addr); std::deque>> data_dequeue; @@ -140,24 +140,31 @@ s32 lv2_socket_p2p::bind(const sys_net_sockaddr& addr) p2p_vport++; } } - else + else if (pport.bound_p2p_vports.contains(p2p_vport)) { - if (pport.bound_p2p_vports.contains(p2p_vport)) + // Check that all other sockets are SO_REUSEADDR or SO_REUSEPORT + auto& bound_sockets = ::at32(pport.bound_p2p_vports, p2p_vport); + if (!sys_net_helpers::all_reusable(bound_sockets)) { return -SYS_NET_EADDRINUSE; } - } - pport.bound_p2p_vports.insert(std::make_pair(p2p_vport, lv2_id)); + bound_sockets.insert(lv2_id); + } + else + { + std::set bound_ports{lv2_id}; + pport.bound_p2p_vports.insert(std::make_pair(p2p_vport, std::move(bound_ports))); + } } } { std::lock_guard lock(mutex); - port = p2p_port; - vport = p2p_vport; - socket = real_socket; - last_bound_addr = addr; + port = p2p_port; + vport = p2p_vport; + socket = real_socket; + bound_addr = psa_in_p2p->sin_addr; } return CELL_OK; @@ -185,15 +192,26 @@ std::pair lv2_socket_p2p::getsockname() return {CELL_OK, sn_addr}; } -std::tuple lv2_socket_p2p::getsockopt([[maybe_unused]] s32 level, [[maybe_unused]] s32 optname, [[maybe_unused]] u32 len) +std::tuple lv2_socket_p2p::getsockopt(s32 level, s32 optname, u32 len) { - // TODO - return {}; + std::lock_guard lock(mutex); + + const u64 key = (static_cast(level) << 32) | static_cast(optname); + + if (!sockopts.contains(key)) + { + sys_net.error("Unhandled getsockopt(level=%d, optname=%d, len=%d)", level, optname, len); + return {}; + } + + const auto& cache = ::at32(sockopts, key); + return {CELL_OK, cache.data, cache.len}; } s32 lv2_socket_p2p::setsockopt(s32 level, s32 optname, const std::vector& optval) { - // TODO + std::lock_guard lock(mutex); + int native_int = *reinterpret_cast*>(optval.data()); if (level == SYS_NET_SOL_SOCKET && optname == SYS_NET_SO_NBIO) @@ -201,7 +219,14 @@ s32 lv2_socket_p2p::setsockopt(s32 level, s32 optname, const std::vector& op so_nbio = native_int; } - return {}; + const u64 key = (static_cast(level) << 32) | static_cast(optname); + sockopt_cache cache{}; + memcpy(&cache.data._int, optval.data(), optval.size()); + cache.len = optval.size(); + + sockopts[key] = std::move(cache); + + return CELL_OK; } std::optional, sys_net_sockaddr>> lv2_socket_p2p::recvfrom(s32 flags, u32 len, bool is_lock) @@ -258,10 +283,12 @@ std::optional lv2_socket_p2p::sendto(s32 flags, const std::vector& buf, inet_ntop(AF_INET, &native_addr.sin_addr, ip_str, sizeof(ip_str)); sys_net.trace("[P2P] Sending a packet to %s:%d:%d", ip_str, p2p_port, p2p_vport); - std::vector p2p_data(buf.size() + sizeof(u16)); + std::vector p2p_data(buf.size() + VPORT_P2P_HEADER_SIZE); const le_t p2p_vport_le = p2p_vport; + const le_t p2p_flags_le = P2P_FLAG_P2P; memcpy(p2p_data.data(), &p2p_vport_le, sizeof(u16)); - memcpy(p2p_data.data() + sizeof(u16), buf.data(), buf.size()); + memcpy(p2p_data.data() + sizeof(u16), &p2p_flags_le, sizeof(u16)); + memcpy(p2p_data.data() + VPORT_P2P_HEADER_SIZE, buf.data(), buf.size()); int native_flags = 0; if (flags & SYS_NET_MSG_WAITALL) @@ -307,7 +334,18 @@ void lv2_socket_p2p::close() auto& p2p_port = ::at32(nc.list_p2p_ports, port); { std::lock_guard lock(p2p_port.bound_p2p_vports_mutex); - p2p_port.bound_p2p_vports.erase(vport); + if (!p2p_port.bound_p2p_vports.contains(vport)) + { + return; + } + + auto& bound_sockets = ::at32(p2p_port.bound_p2p_vports, vport); + bound_sockets.erase(lv2_id); + + if (bound_sockets.empty()) + { + p2p_port.bound_p2p_vports.erase(vport); + } } } } diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h index 5601c28977..110c3404e0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h @@ -41,4 +41,6 @@ protected: u32 bound_addr = 0; // Queue containing received packets from network_thread for SYS_NET_SOCK_DGRAM_P2P sockets std::queue>> data{}; + // List of sock options + std::map sockopts; }; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp index 826ff135c0..09a8c16e6a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp @@ -184,7 +184,7 @@ void initialize_tcp_timeout_monitor() g_fxo->need>(); } -u16 u2s_tcp_checksum(const u16* buffer, usz size) +u16 u2s_tcp_checksum(const le_t* buffer, usz size) { u32 cksum = 0; while (size > 1) @@ -202,20 +202,22 @@ u16 u2s_tcp_checksum(const u16* buffer, usz size) std::vector generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u8* data, const u32 datasize) { - const u32 packet_size = (sizeof(u16) + sizeof(p2ps_encapsulated_tcp) + datasize); + const u32 packet_size = (VPORT_P2P_HEADER_SIZE + sizeof(p2ps_encapsulated_tcp) + datasize); ensure(packet_size < 65535); // packet size shouldn't be bigger than possible UDP payload std::vector packet(packet_size); - u8* packet_data = packet.data(); - le_t dst_port_le = +header.dst_port; + u8* packet_data = packet.data(); + le_t dst_port_le = +header.dst_port; + le_t p2p_flags_le = P2P_FLAG_P2PS; memcpy(packet_data, &dst_port_le, sizeof(u16)); - memcpy(packet_data + sizeof(u16), &header, sizeof(p2ps_encapsulated_tcp)); + memcpy(packet_data + sizeof(u16), &p2p_flags_le, sizeof(u16)); + memcpy(packet_data + VPORT_P2P_HEADER_SIZE, &header, sizeof(p2ps_encapsulated_tcp)); if (datasize) - memcpy(packet_data + sizeof(u16) + sizeof(p2ps_encapsulated_tcp), data, datasize); + memcpy(packet_data + VPORT_P2P_HEADER_SIZE + sizeof(p2ps_encapsulated_tcp), data, datasize); - auto* hdr_ptr = reinterpret_cast(packet_data + sizeof(u16)); + auto* hdr_ptr = reinterpret_cast(packet_data + VPORT_P2P_HEADER_SIZE); hdr_ptr->checksum = 0; - hdr_ptr->checksum = u2s_tcp_checksum(utils::bless(hdr_ptr), sizeof(p2ps_encapsulated_tcp) + datasize); + hdr_ptr->checksum = u2s_tcp_checksum(utils::bless>(hdr_ptr), sizeof(p2ps_encapsulated_tcp) + datasize); return packet; } @@ -225,7 +227,7 @@ lv2_socket_p2ps::lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, { } -lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq) +lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio) : lv2_socket_p2p(SYS_NET_AF_INET, SYS_NET_SOCK_STREAM_P2P, SYS_NET_IPPROTO_IP) { this->socket = socket; @@ -236,6 +238,7 @@ lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op this->op_vport = op_vport; this->cur_seq = cur_seq; this->data_beg_seq = data_beg_seq; + this->so_nbio = so_nbio; status = p2ps_stream_status::stream_connected; } @@ -410,7 +413,7 @@ bool lv2_socket_p2ps::handle_listening(p2ps_encapsulated_tcp* tcp_header, [[mayb const u16 new_op_vport = tcp_header->src_port; const u64 new_cur_seq = send_hdr.seq + 1; const u64 new_data_beg_seq = send_hdr.ack; - auto sock_lv2 = std::make_shared(socket, port, vport, new_op_addr, new_op_port, new_op_vport, new_cur_seq, new_data_beg_seq); + auto sock_lv2 = std::make_shared(socket, port, vport, new_op_addr, new_op_port, new_op_vport, new_cur_seq, new_data_beg_seq, so_nbio); const s32 new_sock_id = idm::import_existing(sock_lv2); sock_lv2->set_lv2_id(new_sock_id); const u64 key_connected = (reinterpret_cast(op_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); @@ -494,6 +497,27 @@ void lv2_socket_p2ps::set_status(p2ps_stream_status new_status) status = new_status; } +std::pair lv2_socket_p2ps::getpeername() +{ + std::lock_guard lock(mutex); + + if (!op_addr || !op_port || !op_vport) + { + return {-SYS_NET_ENOTCONN, {}}; + } + + sys_net_sockaddr res{}; + sys_net_sockaddr_in_p2p* p2p_addr = reinterpret_cast(&res); + + p2p_addr->sin_len = sizeof(sys_net_sockaddr_in_p2p); + p2p_addr->sin_family = SYS_NET_AF_INET; + p2p_addr->sin_addr = std::bit_cast, u32>(op_addr); + p2p_addr->sin_port = op_vport; + p2p_addr->sin_vport = op_port; + + return {CELL_OK, res}; +} + std::tuple, sys_net_sockaddr> lv2_socket_p2ps::accept(bool is_lock) { std::unique_lock lock(mutex, std::defer_lock); @@ -572,26 +596,36 @@ s32 lv2_socket_p2ps::bind(const sys_net_sockaddr& addr) if (p2p_vport == 0) { p2p_vport = 30000; - while (pport.bound_p2p_streams.contains((static_cast(p2p_vport) << 32))) + while (pport.bound_p2ps_vports.contains(p2p_vport)) { p2p_vport++; } - pport.bound_p2p_streams.emplace((static_cast(p2p_vport) << 32), lv2_id); + std::set bound_ports{lv2_id}; + pport.bound_p2ps_vports.insert(std::make_pair(p2p_vport, std::move(bound_ports))); } else { - const u64 key = (static_cast(p2p_vport) << 32); - if (pport.bound_p2p_streams.contains(key)) + if (pport.bound_p2ps_vports.contains(p2p_vport)) { - return -SYS_NET_EADDRINUSE; + auto& bound_sockets = ::at32(pport.bound_p2ps_vports, p2p_vport); + if (!sys_net_helpers::all_reusable(bound_sockets)) + { + return -SYS_NET_EADDRINUSE; + } + + bound_sockets.insert(lv2_id); + } + else + { + std::set bound_ports{lv2_id}; + pport.bound_p2ps_vports.insert(std::make_pair(p2p_vport, std::move(bound_ports))); } - pport.bound_p2p_streams.emplace(key, lv2_id); } - port = p2p_port; - vport = p2p_vport; - socket = real_socket; - last_bound_addr = addr; + port = p2p_port; + vport = p2p_vport; + socket = real_socket; + bound_addr = psa_in_p2p->sin_addr; } } @@ -817,13 +851,24 @@ void lv2_socket_p2ps::close() std::lock_guard lock(p2p_port.bound_p2p_vports_mutex); for (auto it = p2p_port.bound_p2p_streams.begin(); it != p2p_port.bound_p2p_streams.end();) { - if (static_cast(it->second) == lv2_id) + if (it->second == lv2_id) { it = p2p_port.bound_p2p_streams.erase(it); continue; } it++; } + + if (p2p_port.bound_p2ps_vports.contains(vport)) + { + auto& bound_ports = ::at32(p2p_port.bound_p2ps_vports, vport); + bound_ports.erase(lv2_id); + + if (bound_ports.empty()) + { + p2p_port.bound_p2ps_vports.erase(vport); + } + } } } } diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h index ff573ccac2..ec3c3b378d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h @@ -51,14 +51,14 @@ enum p2ps_tcp_flags : u8 }; void initialize_tcp_timeout_monitor(); -u16 u2s_tcp_checksum(const u16* buffer, usz size); +u16 u2s_tcp_checksum(const le_t* buffer, usz size); std::vector generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u8* data, const u32 datasize); class lv2_socket_p2ps final : public lv2_socket_p2p { public: lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); - lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq); + lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio); lv2_socket_p2ps(utils::serial& ar, lv2_socket_type type); void save(utils::serial& ar); @@ -73,6 +73,7 @@ public: std::optional connect(const sys_net_sockaddr& addr) override; + std::pair getpeername() override; std::pair getsockname() override; s32 listen(s32 backlog) override; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp index c2ff4320dc..d63d01fbab 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -13,6 +13,33 @@ LOG_CHANNEL(sys_net); +namespace sys_net_helpers +{ + bool all_reusable(const std::set& sock_ids) + { + for (const s32 sock_id : sock_ids) + { + const auto [_, reusable] = idm::check(sock_id, [&](lv2_socket& sock) -> bool + { + auto [res_reuseaddr, optval_reuseaddr, optlen_reuseaddr] = sock.getsockopt(SYS_NET_SOL_SOCKET, SYS_NET_SO_REUSEADDR, sizeof(s32)); + auto [res_reuseport, optval_reuseport, optlen_reuseport] = sock.getsockopt(SYS_NET_SOL_SOCKET, SYS_NET_SO_REUSEPORT, sizeof(s32)); + + const bool reuse_addr = optlen_reuseaddr == 4 && !!optval_reuseaddr._int; + const bool reuse_port = optlen_reuseport == 4 && !!optval_reuseport._int; + + return (reuse_addr || reuse_port); + }); + + if (!reusable) + { + return false; + } + } + + return true; + } +} // namespace sys_net_helpers + nt_p2p_port::nt_p2p_port(u16 port) : port(port) { @@ -164,6 +191,16 @@ bool nt_p2p_port::recv_data() } } + if (recv_res < VPORT_P2P_HEADER_SIZE) + { + return true; + } + + const u16 vport_flags = *reinterpret_cast*>(p2p_recv_data.data() + sizeof(u16)); + std::vector p2p_data(recv_res - VPORT_P2P_HEADER_SIZE); + memcpy(p2p_data.data(), p2p_recv_data.data() + VPORT_P2P_HEADER_SIZE, p2p_data.size()); + + if (vport_flags & P2P_FLAG_P2P) { std::lock_guard lock(bound_p2p_vports_mutex); if (bound_p2p_vports.contains(dst_vport)) @@ -176,88 +213,96 @@ bool nt_p2p_port::recv_data() p2p_addr.sin_vport = dst_vport; p2p_addr.sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); - std::vector p2p_data(recv_res - sizeof(u16)); - memcpy(p2p_data.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); + auto& bound_sockets = ::at32(bound_p2p_vports, dst_vport); - const auto sock = idm::check(::at32(bound_p2p_vports, dst_vport), [&](lv2_socket& sock) + for (const auto sock_id : bound_sockets) + { + const auto sock = idm::check(sock_id, [&](lv2_socket& sock) + { + ensure(sock.get_type() == SYS_NET_SOCK_DGRAM_P2P); + auto& sock_p2p = reinterpret_cast(sock); + + sock_p2p.handle_new_data(p2p_addr, p2p_data); + }); + + if (!sock) { - ensure(sock.get_type() == SYS_NET_SOCK_DGRAM_P2P); - auto& sock_p2p = reinterpret_cast(sock); - - sock_p2p.handle_new_data(std::move(p2p_addr), std::move(p2p_data)); - }); - - // Should not happen in theory - if (!sock) - bound_p2p_vports.erase(dst_vport); + sys_net.error("Socket %d found in bound_p2p_vports didn't exist!", sock_id); + bound_sockets.erase(sock_id); + if (bound_sockets.empty()) + { + bound_p2p_vports.erase(dst_vport); + } + } + } return true; } } - - // Not directed at a bound DGRAM_P2P vport so check if the packet is a STREAM-P2P packet - - const auto sp_size = recv_res - sizeof(u16); - u8* sp_data = p2p_recv_data.data() + sizeof(u16); - - if (sp_size < sizeof(p2ps_encapsulated_tcp)) + else if (vport_flags & P2P_FLAG_P2PS) { - sys_net.notice("Received P2P packet targeted at unbound vport(likely) or invalid(vport=%d)", dst_vport); - return true; - } - - auto* tcp_header = reinterpret_cast(sp_data); - - // Validate signature & length - if (tcp_header->signature != P2PS_U2S_SIG) - { - sys_net.notice("Received P2P packet targeted at unbound vport(vport=%d)", dst_vport); - return true; - } - - if (tcp_header->length != (sp_size - sizeof(p2ps_encapsulated_tcp))) - { - sys_net.error("Received STREAM-P2P packet tcp length didn't match packet length"); - return true; - } - - // Sanity check - if (tcp_header->dst_port != dst_vport) - { - sys_net.error("Received STREAM-P2P packet with dst_port != vport"); - return true; - } - - // Validate checksum - u16 given_checksum = tcp_header->checksum; - tcp_header->checksum = 0; - if (given_checksum != u2s_tcp_checksum(reinterpret_cast(sp_data), sp_size)) - { - sys_net.error("Checksum is invalid, dropping packet!"); - return true; - } - - // The packet is valid, check if it's bound - const u64 key_connected = (reinterpret_cast(&native_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); - const u64 key_listening = (static_cast(tcp_header->dst_port) << 32); - - { - std::lock_guard lock(bound_p2p_vports_mutex); - if (bound_p2p_streams.contains(key_connected)) + if (p2p_data.size() < sizeof(p2ps_encapsulated_tcp)) { - const auto sock_id = ::at32(bound_p2p_streams, key_connected); - sys_net.trace("Received packet for connected STREAM-P2P socket(s=%d)", sock_id); - handle_connected(sock_id, tcp_header, sp_data + sizeof(p2ps_encapsulated_tcp), &native_addr); + sys_net.notice("Received P2P packet targeted at unbound vport(likely) or invalid(vport=%d)", dst_vport); return true; } - if (bound_p2p_streams.contains(key_listening)) + auto* tcp_header = reinterpret_cast(p2p_data.data()); + + // Validate signature & length + if (tcp_header->signature != P2PS_U2S_SIG) { - const auto sock_id = ::at32(bound_p2p_streams, key_listening); - sys_net.trace("Received packet for listening STREAM-P2P socket(s=%d)", sock_id); - handle_listening(sock_id, tcp_header, sp_data + sizeof(p2ps_encapsulated_tcp), &native_addr); + sys_net.notice("Received P2P packet targeted at unbound vport(vport=%d)", dst_vport); return true; } + + if (tcp_header->length != (p2p_data.size() - sizeof(p2ps_encapsulated_tcp))) + { + sys_net.error("Received STREAM-P2P packet tcp length didn't match packet length"); + return true; + } + + // Sanity check + if (tcp_header->dst_port != dst_vport) + { + sys_net.error("Received STREAM-P2P packet with dst_port != vport"); + return true; + } + + // Validate checksum + u16 given_checksum = tcp_header->checksum; + tcp_header->checksum = 0; + if (given_checksum != u2s_tcp_checksum(reinterpret_cast*>(p2p_data.data()), p2p_data.size())) + { + sys_net.error("Checksum is invalid, dropping packet!"); + return true; + } + + // The packet is valid, check if it's bound + const u64 key_connected = (reinterpret_cast(&native_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); + + { + std::lock_guard lock(bound_p2p_vports_mutex); + if (bound_p2p_streams.contains(key_connected)) + { + const auto sock_id = ::at32(bound_p2p_streams, key_connected); + sys_net.trace("Received packet for connected STREAM-P2P socket(s=%d)", sock_id); + handle_connected(sock_id, tcp_header, p2p_data.data() + sizeof(p2ps_encapsulated_tcp), &native_addr); + return true; + } + + if (bound_p2ps_vports.contains(tcp_header->dst_port)) + { + const auto& bound_sockets = ::at32(bound_p2ps_vports, tcp_header->dst_port); + + for (const auto sock_id : bound_sockets) + { + sys_net.trace("Received packet for listening STREAM-P2P socket(s=%d)", sock_id); + handle_listening(sock_id, tcp_header, p2p_data.data() + sizeof(p2ps_encapsulated_tcp), &native_addr); + } + return true; + } + } } sys_net.notice("Received a STREAM-P2P packet with no bound target"); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h index 33a85df473..c814182544 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "lv2_socket_p2ps.h" #ifdef _WIN32 @@ -18,6 +20,14 @@ #endif #endif +constexpr s32 VPORT_P2P_HEADER_SIZE = sizeof(u16) + sizeof(u16); + +enum VPORT_P2P_FLAGS +{ + P2P_FLAG_P2P = 1, + P2P_FLAG_P2PS = 1 << 1, +}; + struct signaling_message { u32 src_addr = 0; @@ -26,6 +36,11 @@ struct signaling_message std::vector data; }; +namespace sys_net_helpers +{ + bool all_reusable(const std::set& sock_ids); +} + struct nt_p2p_port { // Real socket where P2P packets are received/sent @@ -33,9 +48,11 @@ struct nt_p2p_port u16 port = 0; shared_mutex bound_p2p_vports_mutex; - // For DGRAM_P2P sockets(vport, sock_id) - std::map bound_p2p_vports{}; - // For STREAM_P2P sockets(key, sock_id) + // For DGRAM_P2P sockets (vport, sock_ids) + std::map> bound_p2p_vports{}; + // For STREAM_P2P sockets (vport, sock_ids) + std::map> bound_p2ps_vports{}; + // List of active(either from a connect or an accept) P2PS sockets (key, sock_id) // key is ( (src_vport) << 48 | (dst_vport) << 32 | addr ) with src_vport and addr being 0 for listening sockets std::map bound_p2p_streams{}; diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index c9c90521c0..823e79ad5e 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -71,7 +71,7 @@ namespace rpcn return get_localized_string(rpcn_state_to_localized_string_id(state)); } - constexpr u32 RPCN_PROTOCOL_VERSION = 17; + constexpr u32 RPCN_PROTOCOL_VERSION = 18; constexpr usz RPCN_HEADER_SIZE = 15; constexpr usz COMMUNICATION_ID_SIZE = 9;