fix pings?

This commit is contained in:
lizzie
2026-08-18 01:11:51 +00:00
parent 2a21b0bc22
commit 4f2d0b17c3
3 changed files with 160 additions and 51 deletions
+3 -2
View File
@@ -35,8 +35,8 @@ namespace {
switch (type) { switch (type) {
case Network::Type::STREAM: case Network::Type::STREAM:
case Network::Type::SEQPACKET: case Network::Type::SEQPACKET:
case Network::Type::RAW:
return true; return true;
case Network::Type::RAW:
case Network::Type::DGRAM: case Network::Type::DGRAM:
case Network::Type::RDM: case Network::Type::RDM:
case Network::Type::Unspecified: case Network::Type::Unspecified:
@@ -562,6 +562,7 @@ std::pair<s32, Network::Errno> BSD::SocketImpl(Network::Domain domain, Network::
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6) if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
&& (room_member && room_member->IsConnected())) { && (room_member && room_member->IsConnected())) {
descriptor.socket = std::make_shared<Network::ProxySocket>(); descriptor.socket = std::make_shared<Network::ProxySocket>();
descriptor.socket->fd = fd;
} else { } else {
descriptor.socket = std::make_shared<Network::Socket>(); descriptor.socket = std::make_shared<Network::Socket>();
} }
@@ -573,9 +574,9 @@ std::pair<s32, Network::Errno> BSD::SocketImpl(Network::Domain domain, Network::
&& (protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)) { && (protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)) {
LOG_WARNING(Network, "Using ICMP emulated socket"); LOG_WARNING(Network, "Using ICMP emulated socket");
descriptor.socket = std::make_shared<Network::IcmpSocket>(); descriptor.socket = std::make_shared<Network::IcmpSocket>();
descriptor.socket->fd = fd;
} }
#endif #endif
descriptor.is_connection_based = IsConnectionBased(type); descriptor.is_connection_based = IsConnectionBased(type);
#ifdef _WIN32 #ifdef _WIN32
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) { if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
+138 -47
View File
@@ -1,15 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <cstdio> #include <algorithm>
#include <cerrno>
#include <chrono>
#include <mutex>
#include <thread>
#ifdef __unix__
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#include "common/assert.h" #include "common/assert.h"
#include "common/logging.h" #include "common/logging.h"
#include "core/internal_network/socket_icmp.h" #include "core/internal_network/socket_icmp.h"
#ifdef __unix__ extern "C" {
#include <unistd.h> extern char **environ;
#include <sys/socket.h> }
#endif
namespace Network { namespace Network {
@@ -31,8 +39,6 @@ u16 ComputeChecksum(std::span<const u8> data) {
} }
IcmpSocket::IcmpSocket() noexcept {}
IcmpSocket::~IcmpSocket() { IcmpSocket::~IcmpSocket() {
if (fd == INVALID_SOCKET) { if (fd == INVALID_SOCKET) {
return; return;
@@ -61,6 +67,7 @@ std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) { Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called"); LOG_WARNING(Network, "(stubbed) called");
connected_addr = addr_in;
return Errno::E_SUCCESS; return Errno::E_SUCCESS;
} }
@@ -91,47 +98,69 @@ Errno IcmpSocket::Shutdown(ShutdownHow how) {
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) { std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
LOG_DEBUG(Network, "(stubbed) called"); LOG_DEBUG(Network, "(stubbed) called");
return {s32(0), Errno::E_NOTCONN}; return connected_addr.has_value()
? RecvFrom(flags, message, nullptr)
: std::make_pair(s32(0), Errno::E_NOTCONN);
} }
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) { std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "(stubbed) called"); LOG_DEBUG(Network, "(stubbed) called");
ASSERT(flags == 0); ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)())); ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__)) #if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
if (addr) { const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
if (seq_ident.empty()) const auto timestamp = std::chrono::steady_clock::now();
return {0, Errno::E_SUCCESS}; while (true) {
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT {
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS std::lock_guard guard(pings_mutex);
auto rcv_time = f64(std::min<u64>(rcv_timeo.tv_sec, 600)) + f64(std::min<u64>(rcv_timeo.tv_usec, 1000000)) * f64(1.0 / 1000000.0); // find ping process that is finished running
if (!std::isfinite(rcv_time) || std::isnan(rcv_time)) for (auto it = pings.begin(); it != pings.end();) {
rcv_time = 1.0; pid_t result = waitpid(it->ping_pid, &it->ping_status, WNOHANG);
#ifdef __FreeBSD__ // ping process is still running, go to next
auto const cmd = fmt::format("ping -W {} -o {}.{}.{}.{}", rcv_time, addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]); if (result != it->ping_pid) {
#elif defined(__linux__) ++it;
auto const cmd = fmt::format("ping -W {} -c 1 {}.{}.{}.{}", rcv_time, addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]); continue;
#endif }
if (::system(cmd.c_str()) == 0) { // ping process is finished, remove and handle it
std::vector<u8> data{ it = pings.erase(it);
8, if (it->ping_status == 0) {
0, if (addr) {
0, //checksum addr->family = it->family;
0, addr->ip = it->ip;
u8(seq_ident.front() >> 24), //ident addr->portno = it->portno;
u8(seq_ident.front() >> 16), addr->len = 16;
u8(seq_ident.front() >> 8), // seq addr->zeroes = {};
u8(seq_ident.front() >> 0), }
}; std::array<u8, 8> data{
seq_ident.pop_back(); 0,
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()}); 0,
data[2] = u8(csum >> 8); //hi 0, //checksum
data[3] = u8(csum); //lo 0,
auto const n = (std::max)(data.size(), message.size()); it->seq_ident[0],
std::copy(data.begin(), data.begin() + n, message.begin()); it->seq_ident[1],
return {n, Errno::E_SUCCESS}; it->seq_ident[2],
it->seq_ident[3]
};
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
data[2] = u8(csum >> 8); //hi
data[3] = u8(csum); //lo
auto const n = std::min(data.size(), message.size());
std::copy(data.begin(), data.begin() + n, message.begin());
return {s32(n), Errno::E_SUCCESS};
}
}
} }
return {-1, Errno::E_TIMEDOUT};
if (!blocking)
return {-1, Errno::E_AGAIN};
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
if (time_diff_ms > rcv_timeout_ms)
return {-1, Errno::E_TIMEDOUT};
std::this_thread::yield();
} }
#endif #endif
return {-1, Errno::E_INVAL}; return {-1, Errno::E_INVAL};
@@ -139,12 +168,8 @@ std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Net
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) { std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
LOG_DEBUG(Network, "(stubbed) called"); LOG_DEBUG(Network, "(stubbed) called");
seq_ident.push_back( if (connected_addr.has_value())
(u32(message[4]) << 24) return SendTo(flags, message, std::addressof(connected_addr.value()));
| (u32(message[5]) << 16)
| (u32(message[6]) << 8)
| (u32(message[7]) << 0)
);
return {s32(0), Errno::E_NOTCONN}; return {s32(0), Errno::E_NOTCONN};
} }
@@ -156,8 +181,73 @@ std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message,
// 2..4 -> checksum // 2..4 -> checksum
// 4..6 -> ident // 4..6 -> ident
// 6..8 -> seq // 6..8 -> seq
if (!message.empty())
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
if (!addr)
return {-1, Errno::E_DESTADDRREQ};
if (message.size() >= 8) {
std::string ip_str = fmt::format(
"{}.{}.{}.{}",
addr->ip[0],
addr->ip[1],
addr->ip[2],
addr->ip[3]
);
#ifdef __FreeBSD__
// ping -W option is a nonfractional int (milliseconds)
std::string timeout_str = fmt::format("{}", rcv_timeout_ms);
std::vector<char*> argv = {
const_cast<char*>("ping"),
const_cast<char*>("-c"),
const_cast<char*>("1"),
const_cast<char*>("-W"),
timeout_str.data(),
ip_str.data(),
nullptr
};
#elif defined(__linux__)
// ping -W option is a fractional float (seconds)
auto const rcv_timeout_s = f64(rcv_timeout_ms) / 1000.0;
std::string timeout_str = fmt::format("{}", rcv_timeout_s);
std::vector<char*> argv = {
const_cast<char*>("ping"),
const_cast<char*>("-c"),
const_cast<char*>("1"),
const_cast<char*>("-W"),
timeout_str.data(),
ip_str.data(),
nullptr
};
#endif
pid_t ping_pid;
// we should pass in attributes to stop stdout spam, but im too lazy to figure that out
if (posix_spawnp(&ping_pid, "ping", nullptr, nullptr, argv.data(), environ) != 0) {
LOG_ERROR(Network, "Unable to start ping process for emulated ICMP socket");
return {-1, Errno::E_INVAL};
}
std::lock_guard guard(pings_mutex);
if (pings.size() >= pings.max_size())
pings.erase(pings.begin());
pings.push_back(PingProcessData{
.ip = addr->ip,
.portno = addr->portno,
.ping_pid = ping_pid,
.ping_status = 0,
.seq_ident = {
message[4],
message[5],
message[6],
message[7]
},
.family = addr->family,
});
return {s32(message.size()), Errno::E_SUCCESS}; return {s32(message.size()), Errno::E_SUCCESS};
}
#endif
return {-1, Errno::E_INVAL}; return {-1, Errno::E_INVAL};
} }
@@ -180,6 +270,7 @@ void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
LOG_WARNING(Network, "(stubbed) called"); LOG_WARNING(Network, "(stubbed) called");
} }
Errno IcmpSocket::SetNonBlock(bool enable) { Errno IcmpSocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::E_SUCCESS; return Errno::E_SUCCESS;
} }
+19 -2
View File
@@ -3,15 +3,29 @@
#pragma once #pragma once
#include <array>
#include <span> #include <span>
#include <utility>
#include <sys/types.h>
#include <mutex>
#include <boost/container/static_vector.hpp>
#include "core/internal_network/socket_types.h" #include "core/internal_network/socket_types.h"
#include "core/internal_network/sockets.h" #include "core/internal_network/sockets.h"
namespace Network { namespace Network {
struct PingProcessData {
IPv4Address ip;
u16 portno;
pid_t ping_pid;
pid_t ping_status;
std::array<u8, 4> seq_ident;
u8 family;
};
class IcmpSocket : public Network::SocketBase { class IcmpSocket : public Network::SocketBase {
public: public:
explicit IcmpSocket() noexcept; explicit IcmpSocket() noexcept = default;
~IcmpSocket() override; ~IcmpSocket() override;
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override; Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
Errno Close() override; Errno Close() override;
@@ -32,8 +46,11 @@ public:
void HandleProxyPacket(const ProxyPacket& packet) override; void HandleProxyPacket(const ProxyPacket& packet) override;
Errno SetNonBlock(bool enable) override; Errno SetNonBlock(bool enable) override;
std::vector<u32> seq_ident; boost::container::static_vector<PingProcessData, 128> pings;
std::optional<SockAddrIn> connected_addr;
std::mutex pings_mutex;
Network::Timeval rcv_timeo; Network::Timeval rcv_timeo;
bool blocking = true;
}; };
} // namespace Network } // namespace Network