mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-18 22:23:35 +00:00
fix pings?
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user