589 lines
18 KiB
C++
589 lines
18 KiB
C++
#include <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <set>
|
|
#include <string>
|
|
#include "SceTypes.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
|
|
// Offline network stack: no link is ever up. Sockets are local bookkeeping objects (create/bind/listen/options
|
|
// succeed like they do on a console without a connection), anything that needs a peer fails with the same errno a
|
|
// console with no route gives, and blocking calls return on their timeout, an abort or a close — infinite
|
|
// ("wait forever") timeouts are serviced in bounded slices so the inert stack can never deadlock a caller.
|
|
|
|
namespace {
|
|
// FreeBSD errno numbers as reported through sceNetErrnoLoc (SCE_NET_ERROR_* is 0x80410100 + errno).
|
|
constexpr int NET_ENOENT = 2;
|
|
constexpr int NET_EBADF = 9;
|
|
constexpr int NET_EINVAL = 22;
|
|
constexpr int NET_EAGAIN = 35;
|
|
constexpr int NET_ENOTSOCK = 38;
|
|
constexpr int NET_EOPNOTSUPP = 45;
|
|
constexpr int NET_EPROTONOSUPPORT = 43;
|
|
constexpr int NET_EAFNOSUPPORT = 47;
|
|
constexpr int NET_EADDRINUSE = 48;
|
|
constexpr int NET_ENETUNREACH = 51;
|
|
constexpr int NET_ECONNABORTED = 53;
|
|
constexpr int NET_ENOTCONN = 57;
|
|
constexpr int NET_ETIMEDOUT = 60;
|
|
constexpr int NET_ECONNREFUSED = 61;
|
|
constexpr int NET_ERROR_RESOLVER_ENODNS = static_cast<int>(0x804101E1u);
|
|
|
|
constexpr int NET_AF_INET = 2;
|
|
constexpr int NET_AF_INET6 = 28;
|
|
constexpr int NET_SOCK_STREAM = 1;
|
|
constexpr int NET_SOCK_DGRAM = 2;
|
|
constexpr int NET_SOCK_RAW = 3;
|
|
constexpr int NET_SOL_SOCKET = 0xFFFF;
|
|
constexpr int NET_SO_SNDTIMEO = 0x1005;
|
|
constexpr int NET_SO_RCVTIMEO = 0x1006;
|
|
constexpr int NET_SO_NBIO = 0x1200;
|
|
|
|
struct Sock {
|
|
int family = 0;
|
|
int type = 0;
|
|
bool nonblock = false;
|
|
bool bound = false;
|
|
bool listening = false;
|
|
bool aborted = false;
|
|
std::uint16_t port = 0; // network byte order
|
|
std::uint32_t addr = 0; // network byte order
|
|
int rcv_timeout_us = 0;
|
|
int snd_timeout_us = 0;
|
|
};
|
|
|
|
std::mutex g_mutex;
|
|
std::condition_variable g_cv;
|
|
std::map<int, Sock> g_socks;
|
|
std::set<int> g_epolls;
|
|
std::set<int> g_pools;
|
|
std::set<int> g_resolvers;
|
|
int g_next_sock = 32;
|
|
int g_next_epoll = 0x4000;
|
|
int g_next_pool = 1;
|
|
int g_next_resolver = 0x100;
|
|
std::uint16_t g_next_port = 49152;
|
|
bool g_net_inited = false;
|
|
|
|
int* errno_slot() {
|
|
static thread_local int e = 0;
|
|
return &e;
|
|
}
|
|
|
|
int fail(int err) {
|
|
*errno_slot() = err;
|
|
return -1;
|
|
}
|
|
|
|
void log_soft(const char* func, const char* what) {
|
|
static std::mutex mtx;
|
|
static std::map<std::string, int> hits;
|
|
std::lock_guard<std::mutex> lk(mtx);
|
|
if (++hits[func] > 3) {
|
|
return;
|
|
}
|
|
std::fprintf(stderr, "[SOFT-NET] %s -> %s\n", func, what);
|
|
std::fflush(stderr);
|
|
}
|
|
|
|
std::uint16_t swap16(std::uint16_t v) { return static_cast<std::uint16_t>((v << 8) | (v >> 8)); }
|
|
std::uint32_t swap32(std::uint32_t v) {
|
|
return (v << 24) | ((v & 0xFF00u) << 8) | ((v >> 8) & 0xFF00u) | (v >> 24);
|
|
}
|
|
|
|
// Waits on the socket until the timeout expires or it is closed/aborted.
|
|
// timeout_us <= 0 nominally means "wait forever", but on an inert stack nothing can ever arrive, so such a
|
|
// wait is bounded to INFINITE_WAIT_SLICE_US per iteration and then reports EAGAIN: the caller keeps correct
|
|
// blocking-loop semantics without the process being able to deadlock on the network.
|
|
// Returns 0 on timeout, -1 with errno set when the socket went away or was aborted.
|
|
int block_on(std::unique_lock<std::mutex>& lk, int fd, int timeout_us) {
|
|
constexpr int INFINITE_WAIT_SLICE_US = 100000; // 100 ms
|
|
const auto deadline = std::chrono::steady_clock::now()
|
|
+ std::chrono::microseconds(timeout_us > 0 ? timeout_us : INFINITE_WAIT_SLICE_US);
|
|
for (;;) {
|
|
auto it = g_socks.find(fd);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (it->second.aborted) {
|
|
it->second.aborted = false;
|
|
return fail(NET_ECONNABORTED);
|
|
}
|
|
if (g_cv.wait_until(lk, deadline) == std::cv_status::timeout) {
|
|
return fail(NET_EAGAIN);
|
|
}
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
extern "C" {
|
|
|
|
int* APS5_VABI sceNetErrnoLoc(void) {
|
|
return errno_slot();
|
|
}
|
|
|
|
int APS5_VABI sceNetInit_nid_postfix(void) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
g_net_inited = true;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetTerm(void) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
g_net_inited = false;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetPoolCreate(const char* name, int size, int flags) {
|
|
(void)name;
|
|
(void)flags;
|
|
if (size <= 0) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
const int id = g_next_pool++;
|
|
g_pools.insert(id);
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceNetPoolDestroy(int memid) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
return g_pools.erase(memid) != 0 ? 0 : fail(NET_EBADF);
|
|
}
|
|
|
|
int APS5_VABI sceNetSocket(const char* name, int family, int type, int protocol) {
|
|
(void)name;
|
|
(void)protocol;
|
|
if (family != NET_AF_INET && family != NET_AF_INET6) {
|
|
return fail(NET_EAFNOSUPPORT);
|
|
}
|
|
if (type != NET_SOCK_STREAM && type != NET_SOCK_DGRAM && type != NET_SOCK_RAW) {
|
|
return fail(NET_EPROTONOSUPPORT);
|
|
}
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
const int fd = g_next_sock++;
|
|
Sock s;
|
|
s.family = family;
|
|
s.type = type;
|
|
g_socks[fd] = s;
|
|
return fd;
|
|
}
|
|
|
|
int APS5_VABI sceNetSocketClose(int s) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
if (g_socks.erase(s) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
g_cv.notify_all();
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetSocketAbort(int s, int flags) {
|
|
(void)flags;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
it->second.aborted = true;
|
|
g_cv.notify_all();
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetBind_nid_postfix(int s, const void* addr, uint32_t addrlen) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (addr == nullptr || addrlen < 8) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
if (it->second.bound) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
// sockaddr_in: len, family, port (BE), addr (BE)
|
|
std::uint16_t port = 0;
|
|
std::uint32_t ip = 0;
|
|
std::memcpy(&port, static_cast<const std::uint8_t*>(addr) + 2, 2);
|
|
std::memcpy(&ip, static_cast<const std::uint8_t*>(addr) + 4, 4);
|
|
if (port == 0) {
|
|
port = swap16(g_next_port++);
|
|
} else {
|
|
for (const auto& kv : g_socks) {
|
|
if (kv.first != s && kv.second.bound && kv.second.port == port && kv.second.type == it->second.type) {
|
|
return fail(NET_EADDRINUSE);
|
|
}
|
|
}
|
|
}
|
|
it->second.bound = true;
|
|
it->second.port = port;
|
|
it->second.addr = ip;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetListen(int s, int backlog) {
|
|
(void)backlog;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (it->second.type != NET_SOCK_STREAM) {
|
|
return fail(NET_EOPNOTSUPP);
|
|
}
|
|
if (!it->second.bound) {
|
|
it->second.bound = true;
|
|
it->second.port = swap16(g_next_port++);
|
|
}
|
|
it->second.listening = true;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetAccept(int s, void* addr, uint32_t* addrlen) {
|
|
(void)addr;
|
|
(void)addrlen;
|
|
std::unique_lock<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (!it->second.listening) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
if (it->second.nonblock) {
|
|
return fail(NET_EAGAIN);
|
|
}
|
|
// No peer can ever arrive: wait for close/abort like an idle listener.
|
|
const int rc = block_on(lk, s, it->second.rcv_timeout_us);
|
|
return rc;
|
|
}
|
|
|
|
int APS5_VABI sceNetConnect(int s, const void* addr, uint32_t addrlen) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (addr == nullptr || addrlen < 8) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
std::uint32_t ip = 0;
|
|
std::memcpy(&ip, static_cast<const std::uint8_t*>(addr) + 4, 4);
|
|
if ((swap32(ip) >> 24) == 127) {
|
|
return fail(NET_ECONNREFUSED); // loopback with no listener
|
|
}
|
|
return fail(NET_ENETUNREACH);
|
|
}
|
|
|
|
static int recv_common(int s) {
|
|
std::unique_lock<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (it->second.type == NET_SOCK_STREAM) {
|
|
return fail(NET_ENOTCONN);
|
|
}
|
|
if (it->second.nonblock) {
|
|
return fail(NET_EAGAIN);
|
|
}
|
|
return block_on(lk, s, it->second.rcv_timeout_us);
|
|
}
|
|
|
|
int64_t APS5_VABI sceNetRecv(int s, void* buf, size_t len, int flags) {
|
|
(void)buf;
|
|
(void)len;
|
|
(void)flags;
|
|
return recv_common(s);
|
|
}
|
|
|
|
int64_t APS5_VABI sceNetRecvfrom(int s, void* buf, size_t len, int flags, void* from, uint32_t* fromlen) {
|
|
(void)buf;
|
|
(void)len;
|
|
(void)flags;
|
|
(void)from;
|
|
(void)fromlen;
|
|
return recv_common(s);
|
|
}
|
|
|
|
static int send_common(int s) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
return fail(it->second.type == NET_SOCK_STREAM ? NET_ENOTCONN : NET_ENETUNREACH);
|
|
}
|
|
|
|
int64_t APS5_VABI sceNetSend(int s, const void* buf, size_t len, int flags) {
|
|
(void)buf;
|
|
(void)len;
|
|
(void)flags;
|
|
return send_common(s);
|
|
}
|
|
|
|
int64_t APS5_VABI sceNetSendto(int s, const void* buf, size_t len, int flags, const void* to, uint32_t tolen) {
|
|
(void)buf;
|
|
(void)len;
|
|
(void)flags;
|
|
(void)to;
|
|
(void)tolen;
|
|
return send_common(s);
|
|
}
|
|
|
|
int APS5_VABI sceNetShutdown(int s, int how) {
|
|
(void)how;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
return it->second.type == NET_SOCK_STREAM ? fail(NET_ENOTCONN) : 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetSetsockopt(int s, int level, int optname, const void* optval, uint32_t optlen) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (level == NET_SOL_SOCKET && optval != nullptr && optlen >= sizeof(int)) {
|
|
int v = 0;
|
|
std::memcpy(&v, optval, sizeof(v));
|
|
switch (optname) {
|
|
case NET_SO_NBIO: it->second.nonblock = v != 0; break;
|
|
case NET_SO_RCVTIMEO: it->second.rcv_timeout_us = v; break;
|
|
case NET_SO_SNDTIMEO: it->second.snd_timeout_us = v; break;
|
|
default: break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetGetsockopt(int s, int level, int optname, void* optval, uint32_t* optlen) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (optval == nullptr || optlen == nullptr || *optlen < sizeof(int)) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
int v = 0;
|
|
if (level == NET_SOL_SOCKET) {
|
|
switch (optname) {
|
|
case NET_SO_NBIO: v = it->second.nonblock ? 1 : 0; break;
|
|
case NET_SO_RCVTIMEO: v = it->second.rcv_timeout_us; break;
|
|
case NET_SO_SNDTIMEO: v = it->second.snd_timeout_us; break;
|
|
default: break;
|
|
}
|
|
}
|
|
std::memcpy(optval, &v, sizeof(v));
|
|
*optlen = sizeof(v);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetGetsockname(int s, void* addr, uint32_t* addrlen) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
auto it = g_socks.find(s);
|
|
if (it == g_socks.end()) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (addr == nullptr || addrlen == nullptr) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
std::uint8_t sa[16] = {};
|
|
sa[0] = 16;
|
|
sa[1] = static_cast<std::uint8_t>(NET_AF_INET);
|
|
std::memcpy(sa + 2, &it->second.port, 2);
|
|
std::memcpy(sa + 4, &it->second.addr, 4);
|
|
const std::uint32_t n = *addrlen < sizeof(sa) ? *addrlen : static_cast<std::uint32_t>(sizeof(sa));
|
|
std::memcpy(addr, sa, n);
|
|
*addrlen = sizeof(sa);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetGetSockInfo(int s, void* info, int n, int flags) {
|
|
(void)info;
|
|
(void)n;
|
|
(void)flags;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
return g_socks.count(s) != 0 ? 0 : fail(NET_EBADF);
|
|
}
|
|
|
|
// Epoll: nothing can become ready without a network, so a wait sleeps for its timeout (microseconds; a
|
|
// negative infinite timeout is serviced in bounded slices) and reports zero events.
|
|
int APS5_VABI sceNetEpollCreate(const char* name, int flags) {
|
|
(void)name;
|
|
(void)flags;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
const int id = g_next_epoll++;
|
|
g_epolls.insert(id);
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceNetEpollDestroy(int eid) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
if (g_epolls.erase(eid) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
g_cv.notify_all();
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetEpollControl(int eid, int op, int id, const NetEpollEvent* event) {
|
|
(void)event;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
if (g_epolls.count(eid) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (g_socks.count(id) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (op < 1 || op > 3) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetEpollWait(int eid, NetEpollEvent* events, int maxevents, int timeout) {
|
|
(void)events;
|
|
if (maxevents <= 0) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
std::unique_lock<std::mutex> lk(g_mutex);
|
|
if (g_epolls.count(eid) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
if (timeout == 0) {
|
|
return 0;
|
|
}
|
|
// timeout < 0 nominally waits until the epoll object is destroyed; bound each wait slice so an
|
|
// infinite-timeout waiter on the inert stack still returns periodically (0 events) instead of
|
|
// deadlocking if nobody ever destroys the epoll.
|
|
constexpr int INFINITE_WAIT_SLICE_US = 100000; // 100 ms
|
|
const auto deadline = std::chrono::steady_clock::now()
|
|
+ std::chrono::microseconds(timeout > 0 ? timeout : INFINITE_WAIT_SLICE_US);
|
|
for (;;) {
|
|
if (g_cv.wait_until(lk, deadline) == std::cv_status::timeout) {
|
|
return 0;
|
|
}
|
|
if (g_epolls.count(eid) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Byte order and text conversion.
|
|
uint32_t APS5_VABI sceNetHtonl_nid_postfix(uint32_t host32) { return swap32(host32); }
|
|
uint16_t APS5_VABI sceNetHtons_nid_postfix(uint16_t host16) { return swap16(host16); }
|
|
uint32_t APS5_VABI sceNetNtohl_nid_postfix(uint32_t net32) { return swap32(net32); }
|
|
uint16_t APS5_VABI sceNetNtohs_nid_postfix(uint16_t net16) { return swap16(net16); }
|
|
|
|
int APS5_VABI sceNetInetPton(int af, const char* src, void* dst) {
|
|
if (src == nullptr || dst == nullptr) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
if (af != NET_AF_INET) {
|
|
return fail(NET_EAFNOSUPPORT);
|
|
}
|
|
unsigned a = 0, b = 0, c = 0, d = 0;
|
|
char tail = 0;
|
|
if (std::sscanf(src, "%3u.%3u.%3u.%3u%c", &a, &b, &c, &d, &tail) != 4 || a > 255 || b > 255 || c > 255 || d > 255) {
|
|
return 0;
|
|
}
|
|
const std::uint8_t out[4] = {static_cast<std::uint8_t>(a), static_cast<std::uint8_t>(b), static_cast<std::uint8_t>(c), static_cast<std::uint8_t>(d)};
|
|
std::memcpy(dst, out, 4);
|
|
return 1;
|
|
}
|
|
|
|
const char* APS5_VABI sceNetInetNtop(int af, const void* src, char* dst, uint32_t size) {
|
|
if (src == nullptr || dst == nullptr || af != NET_AF_INET) {
|
|
*errno_slot() = af != NET_AF_INET ? NET_EAFNOSUPPORT : NET_EINVAL;
|
|
return nullptr;
|
|
}
|
|
const auto* p = static_cast<const std::uint8_t*>(src);
|
|
char tmp[16];
|
|
const int n = std::snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
|
|
if (n < 0 || static_cast<uint32_t>(n) >= size) {
|
|
*errno_slot() = 28; // ENOSPC
|
|
return nullptr;
|
|
}
|
|
std::memcpy(dst, tmp, static_cast<std::size_t>(n) + 1);
|
|
return dst;
|
|
}
|
|
|
|
int APS5_VABI sceNetEtherNtostr(const NetEtherAddr* n, char* str, size_t len) {
|
|
if (n == nullptr || str == nullptr || len < 18) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
std::snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x", n->data[0], n->data[1], n->data[2], n->data[3], n->data[4], n->data[5]);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceNetGetMacAddress(NetEtherAddr* addr, int flags) {
|
|
(void)flags;
|
|
if (addr == nullptr) {
|
|
return fail(NET_EINVAL);
|
|
}
|
|
// Locally administered address; there is no adapter to read one from.
|
|
const std::uint8_t mac[6] = {0x02, 0x50, 0x53, 0x35, 0x00, 0x01};
|
|
std::memcpy(addr->data, mac, 6);
|
|
return 0;
|
|
}
|
|
|
|
// Resolver: there is no DNS server to ask.
|
|
int APS5_VABI sceNetResolverCreate(const char* name, int memid, int flags) {
|
|
(void)name;
|
|
(void)memid;
|
|
(void)flags;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
const int id = g_next_resolver++;
|
|
g_resolvers.insert(id);
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceNetResolverDestroy(int rid) {
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
return g_resolvers.erase(rid) != 0 ? 0 : fail(NET_EBADF);
|
|
}
|
|
|
|
int APS5_VABI sceNetResolverStartNtoa(int rid, const char* hostname, void* addr, int timeout, int retry, int flags) {
|
|
(void)hostname;
|
|
(void)addr;
|
|
(void)timeout;
|
|
(void)retry;
|
|
(void)flags;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
if (g_resolvers.count(rid) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
log_soft(__func__, "ENODNS (no network)");
|
|
*errno_slot() = NET_ETIMEDOUT;
|
|
return NET_ERROR_RESOLVER_ENODNS;
|
|
}
|
|
|
|
int APS5_VABI sceNetResolverStartAton(int rid, const void* addr, char* hostname, int len, int timeout, int retry, int flags) {
|
|
(void)addr;
|
|
(void)hostname;
|
|
(void)len;
|
|
(void)timeout;
|
|
(void)retry;
|
|
(void)flags;
|
|
std::lock_guard<std::mutex> lk(g_mutex);
|
|
if (g_resolvers.count(rid) == 0) {
|
|
return fail(NET_EBADF);
|
|
}
|
|
log_soft(__func__, "ENODNS (no network)");
|
|
*errno_slot() = NET_ETIMEDOUT;
|
|
return NET_ERROR_RESOLVER_ENODNS;
|
|
}
|
|
}
|