fix pings?

This commit is contained in:
lizzie
2026-08-18 01:11:51 +00:00
parent 3ff405a151
commit ce0b278fd8
3 changed files with 160 additions and 51 deletions
+3 -2
View File
@@ -35,8 +35,8 @@ namespace {
switch (type) {
case Network::Type::STREAM:
case Network::Type::SEQPACKET:
case Network::Type::RAW:
return true;
case Network::Type::RAW:
case Network::Type::DGRAM:
case Network::Type::RDM:
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)
&& (room_member && room_member->IsConnected())) {
descriptor.socket = std::make_shared<Network::ProxySocket>();
descriptor.socket->fd = fd;
} else {
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)) {
LOG_WARNING(Network, "Using ICMP emulated socket");
descriptor.socket = std::make_shared<Network::IcmpSocket>();
descriptor.socket->fd = fd;
}
#endif
descriptor.is_connection_based = IsConnectionBased(type);
#ifdef _WIN32
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-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/logging.h"
#include "core/internal_network/socket_icmp.h"
#ifdef __unix__
#include <unistd.h>
#include <sys/socket.h>
#endif
extern "C" {
extern char **environ;
}
namespace Network {
@@ -31,8 +39,6 @@ u16 ComputeChecksum(std::span<const u8> data) {
}
IcmpSocket::IcmpSocket() noexcept {}
IcmpSocket::~IcmpSocket() {
if (fd == INVALID_SOCKET) {
return;
@@ -61,6 +67,7 @@ std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
connected_addr = addr_in;
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) {
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) {
LOG_DEBUG(Network, "(stubbed) called");
ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
if (addr) {
if (seq_ident.empty())
return {0, Errno::E_SUCCESS};
// 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
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);
if (!std::isfinite(rcv_time) || std::isnan(rcv_time))
rcv_time = 1.0;
#ifdef __FreeBSD__
auto const cmd = fmt::format("ping -W {} -o {}.{}.{}.{}", rcv_time, addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]);
#elif defined(__linux__)
auto const cmd = fmt::format("ping -W {} -c 1 {}.{}.{}.{}", rcv_time, addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]);
#endif
if (::system(cmd.c_str()) == 0) {
std::vector<u8> data{
8,
0,
0, //checksum
0,
u8(seq_ident.front() >> 24), //ident
u8(seq_ident.front() >> 16),
u8(seq_ident.front() >> 8), // seq
u8(seq_ident.front() >> 0),
};
seq_ident.pop_back();
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::max)(data.size(), message.size());
std::copy(data.begin(), data.begin() + n, message.begin());
return {n, Errno::E_SUCCESS};
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
const auto timestamp = std::chrono::steady_clock::now();
while (true) {
{
std::lock_guard guard(pings_mutex);
// find ping process that is finished running
for (auto it = pings.begin(); it != pings.end();) {
pid_t result = waitpid(it->ping_pid, &it->ping_status, WNOHANG);
// ping process is still running, go to next
if (result != it->ping_pid) {
++it;
continue;
}
// ping process is finished, remove and handle it
it = pings.erase(it);
if (it->ping_status == 0) {
if (addr) {
addr->family = it->family;
addr->ip = it->ip;
addr->portno = it->portno;
addr->len = 16;
addr->zeroes = {};
}
std::array<u8, 8> data{
0,
0,
0, //checksum
0,
it->seq_ident[0],
it->seq_ident[1],
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
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) {
LOG_DEBUG(Network, "(stubbed) called");
seq_ident.push_back(
(u32(message[4]) << 24)
| (u32(message[5]) << 16)
| (u32(message[6]) << 8)
| (u32(message[7]) << 0)
);
if (connected_addr.has_value())
return SendTo(flags, message, std::addressof(connected_addr.value()));
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
// 4..6 -> ident
// 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};
}
#endif
return {-1, Errno::E_INVAL};
}
@@ -180,6 +270,7 @@ void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
LOG_WARNING(Network, "(stubbed) called");
}
Errno IcmpSocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::E_SUCCESS;
}
+19 -2
View File
@@ -3,15 +3,29 @@
#pragma once
#include <array>
#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/sockets.h"
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 {
public:
explicit IcmpSocket() noexcept;
explicit IcmpSocket() noexcept = default;
~IcmpSocket() override;
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
Errno Close() override;
@@ -32,8 +46,11 @@ public:
void HandleProxyPacket(const ProxyPacket& packet) 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;
bool blocking = true;
};
} // namespace Network