mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
4060 lines
102 KiB
C++
4060 lines
102 KiB
C++
#if defined(_WIN32)
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <winsock2.h>
|
|
#include <windows.h>
|
|
#include <iphlpapi.h>
|
|
#include <ws2tcpip.h>
|
|
#ifdef s_addr
|
|
#undef s_addr
|
|
#endif
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <netdb.h>
|
|
#include <netinet/tcp.h>
|
|
#include <sys/select.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "common/assert.h"
|
|
#include "common/common.h"
|
|
#include "common/emulatorConfig.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/threads.h"
|
|
#include "kernel/pthread.h"
|
|
#include "libs/errno.h"
|
|
#include "libs/libs.h"
|
|
#include "libs/network.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <fmt/format.h>
|
|
#include <fmt/ranges.h>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace Libs::Network {
|
|
|
|
class Network {
|
|
public:
|
|
class Id {
|
|
public:
|
|
static constexpr int MAX_ID = 65536;
|
|
|
|
enum class Type : uint32_t {
|
|
Invalid = 0,
|
|
Http = 1,
|
|
Ssl = 2,
|
|
Template = 3,
|
|
Connection = 4,
|
|
Request = 5,
|
|
};
|
|
|
|
explicit Id(int id)
|
|
: m_id(static_cast<uint32_t>(id) & 0xffffu), m_type(static_cast<uint32_t>(id) >> 16u) {}
|
|
[[nodiscard]] int ToInt() const {
|
|
return static_cast<int>(m_id + (static_cast<uint32_t>(m_type) << 16u));
|
|
}
|
|
[[nodiscard]] bool IsValid() const { return GetType() != Type::Invalid; }
|
|
[[nodiscard]] Type GetType() const {
|
|
switch (m_type) {
|
|
case static_cast<uint32_t>(Type::Http): return Type::Http;
|
|
case static_cast<uint32_t>(Type::Ssl): return Type::Ssl;
|
|
case static_cast<uint32_t>(Type::Template): return Type::Template;
|
|
case static_cast<uint32_t>(Type::Connection): return Type::Connection;
|
|
case static_cast<uint32_t>(Type::Request): return Type::Request;
|
|
default: return Type::Invalid;
|
|
}
|
|
}
|
|
|
|
friend class Network;
|
|
|
|
private:
|
|
Id() = default;
|
|
static Id Invalid() { return {}; }
|
|
static Id Create(int net_id, Type type) {
|
|
Id r;
|
|
r.m_id = net_id;
|
|
r.m_type = static_cast<uint32_t>(type);
|
|
return r;
|
|
}
|
|
[[nodiscard]] int GetId() const { return static_cast<int>(m_id); }
|
|
|
|
uint32_t m_id = 0;
|
|
uint32_t m_type = static_cast<uint32_t>(Type::Invalid);
|
|
};
|
|
|
|
using HttpsCallback = KYTY_SYSV_ABI int (*)(int, unsigned int, void* const*, int, void*);
|
|
|
|
Network() = default;
|
|
virtual ~Network() = default;
|
|
|
|
KYTY_CLASS_NO_COPY(Network);
|
|
|
|
int PoolCreate(const char* name, int size);
|
|
bool PoolDestroy(int memid);
|
|
int ResolverCreate(const char* name, int memid);
|
|
bool ResolverDestroy(int rid);
|
|
bool ResolverValid(int rid);
|
|
|
|
Id SslInit(uint64_t pool_size);
|
|
bool SslTerm(Id ssl_ctx_id);
|
|
bool SslValid(Id ssl_ctx_id);
|
|
|
|
Id HttpInit(int memid, Id ssl_ctx_id, uint64_t pool_size);
|
|
bool HttpTerm(Id http_ctx_id);
|
|
Id HttpCreateTemplate(Id http_ctx_id, const char* user_agent, int http_ver,
|
|
bool is_auto_proxy_conf);
|
|
bool HttpDeleteTemplate(Id tmpl_id);
|
|
bool HttpSetNonblock(Id id, bool enable);
|
|
bool HttpsSetSslCallback(Id id, HttpsCallback cbfunc, void* user_arg);
|
|
bool HttpsSetMinSslVersion(Id id, uint32_t ssl_version);
|
|
bool HttpsDisableOption(Id id, uint32_t ssl_flags);
|
|
bool HttpAddRequestHeader(Id id, const char* name, const char* value, bool add);
|
|
bool HttpValid(Id http_ctx_id);
|
|
bool HttpValidTemplate(Id tmpl_id);
|
|
bool HttpValidConnection(Id conn_id);
|
|
bool HttpValidRequest(Id req_id);
|
|
Id HttpCreateConnection(Id tmpl_id, const char* server_name, const char* scheme, uint16_t port,
|
|
bool enable_keep_alive);
|
|
Id HttpCreateConnectionWithURL(Id tmpl_id, const char* url, bool enable_keep_alive);
|
|
bool HttpDeleteConnection(Id conn_id);
|
|
Id HttpCreateRequestWithURL2(Id conn_id, const char* method, const char* url,
|
|
uint64_t content_length);
|
|
bool HttpSetRequestContentLength(Id req_id, uint64_t content_length);
|
|
bool HttpDeleteRequest(Id req_id);
|
|
bool HttpSetResolveTimeOut(Id id, uint32_t usec);
|
|
bool HttpSetResolveRetry(Id id, int32_t retry);
|
|
bool HttpSetConnectTimeOut(Id id, uint32_t usec);
|
|
bool HttpSetSendTimeOut(Id id, uint32_t usec);
|
|
bool HttpSetRecvTimeOut(Id id, uint32_t usec);
|
|
bool HttpSetAutoRedirect(Id id, int enable);
|
|
bool HttpSetAuthEnabled(Id id, int enable);
|
|
bool HttpMarkRequestSent(Id req_id, int result);
|
|
bool HttpGetRequestResponse(Id req_id, int* send_result, int* status_code, const char** headers,
|
|
size_t* headers_size, uint64_t* content_length);
|
|
|
|
private:
|
|
struct Pool {
|
|
bool used = false;
|
|
std::string name;
|
|
int size = 0;
|
|
};
|
|
|
|
struct Ssl {
|
|
bool used = false;
|
|
uint64_t size = 0;
|
|
};
|
|
|
|
struct Resolver {
|
|
bool used = false;
|
|
std::string name;
|
|
int memid = 0;
|
|
};
|
|
|
|
struct Http {
|
|
bool used = false;
|
|
uint64_t size = 0;
|
|
int memid = 0;
|
|
int ssl_ctx_id = 0;
|
|
};
|
|
|
|
struct HttpHeader {
|
|
std::string name;
|
|
std::string value;
|
|
};
|
|
|
|
struct HttpBase {
|
|
std::vector<HttpHeader> headers;
|
|
bool used = false;
|
|
bool nonblock = false;
|
|
bool auto_redirect = true;
|
|
bool auth_enabled = true;
|
|
HttpsCallback ssl_cbfunc = nullptr;
|
|
void* ssl_user_arg = nullptr;
|
|
uint32_t ssl_flags = 0xA7;
|
|
uint32_t min_ssl_version = 0;
|
|
int http_ctx_id = 0;
|
|
uint32_t resolve_timeout = 1'000000;
|
|
int32_t resolve_retry = 4;
|
|
uint32_t connect_timeout = 30'000000;
|
|
uint32_t send_timeout = 120'000000;
|
|
uint32_t recv_timeout = 120'000000;
|
|
};
|
|
|
|
struct HttpTemplate: public HttpBase {
|
|
std::string user_agent;
|
|
int http_ver = 0;
|
|
bool is_auto_proxy_conf = true;
|
|
};
|
|
|
|
struct HttpConnection: public HttpTemplate {
|
|
explicit HttpConnection(const HttpTemplate& tmpl): HttpTemplate(tmpl) {}
|
|
// int tmpl_id = 0;
|
|
std::string url;
|
|
bool enable_keep_alive = false;
|
|
};
|
|
|
|
struct HttpRequest: public HttpConnection {
|
|
explicit HttpRequest(HttpConnection& conn): HttpConnection(conn) {}
|
|
// int conn_id = 0;
|
|
std::string method;
|
|
std::string url;
|
|
uint64_t content_length = 0;
|
|
bool send_attempted = false;
|
|
int send_result = HTTP_ERROR_BEFORE_SEND;
|
|
int status_code = 0;
|
|
std::string response_headers;
|
|
};
|
|
|
|
HttpBase* FindHttpBase(Id id, bool include_request);
|
|
|
|
static constexpr int POOLS_MAX = 32;
|
|
static constexpr int RESOLVERS_MAX = 32;
|
|
static constexpr int SSL_MAX = 32;
|
|
static constexpr int HTTP_MAX = 32;
|
|
|
|
Common::Mutex m_mutex;
|
|
Pool m_pools[POOLS_MAX];
|
|
Resolver m_resolvers[RESOLVERS_MAX];
|
|
Ssl m_ssl[SSL_MAX];
|
|
Http m_http[HTTP_MAX];
|
|
std::vector<HttpTemplate> m_templates;
|
|
std::vector<HttpConnection> m_connections;
|
|
std::vector<HttpRequest> m_requests;
|
|
};
|
|
|
|
static Network* g_net = nullptr;
|
|
|
|
void Initialize() {
|
|
EXIT_IF(g_net != nullptr);
|
|
|
|
g_net = new Network;
|
|
}
|
|
|
|
void Shutdown() {
|
|
delete g_net;
|
|
g_net = nullptr;
|
|
}
|
|
|
|
int Network::PoolCreate(const char* name, int size) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
for (int id = 0; id < POOLS_MAX; id++) {
|
|
if (!m_pools[id].used) {
|
|
m_pools[id].used = true;
|
|
m_pools[id].size = size;
|
|
m_pools[id].name = std::string(name);
|
|
|
|
return id;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool Network::PoolDestroy(int memid) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (memid >= 0 && memid < POOLS_MAX && m_pools[memid].used) {
|
|
m_pools[memid].used = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int Network::ResolverCreate(const char* name, int memid) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (memid < 0 || memid >= POOLS_MAX || !m_pools[memid].used) {
|
|
return -1;
|
|
}
|
|
|
|
for (int id = 0; id < RESOLVERS_MAX; id++) {
|
|
if (!m_resolvers[id].used) {
|
|
m_resolvers[id].used = true;
|
|
m_resolvers[id].name = std::string(name);
|
|
m_resolvers[id].memid = memid;
|
|
|
|
return id;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool Network::ResolverDestroy(int rid) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (rid >= 0 && rid < RESOLVERS_MAX && m_resolvers[rid].used) {
|
|
m_resolvers[rid] = {};
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::ResolverValid(int rid) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
return rid >= 0 && rid < RESOLVERS_MAX && m_resolvers[rid].used;
|
|
}
|
|
|
|
Network::Id Network::SslInit(uint64_t pool_size) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
for (int id = 0; id < SSL_MAX; id++) {
|
|
if (!m_ssl[id].used) {
|
|
m_ssl[id].used = true;
|
|
m_ssl[id].size = pool_size;
|
|
|
|
return Id::Create(id, Id::Type::Ssl);
|
|
}
|
|
}
|
|
|
|
return Id::Invalid();
|
|
}
|
|
|
|
bool Network::SslTerm(Id ssl_ctx_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (ssl_ctx_id.GetType() == Id::Type::Ssl && ssl_ctx_id.GetId() >= 0 &&
|
|
ssl_ctx_id.GetId() < SSL_MAX && m_ssl[ssl_ctx_id.GetId()].used) {
|
|
m_ssl[ssl_ctx_id.GetId()].used = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::SslValid(Id ssl_ctx_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
return ssl_ctx_id.GetType() == Id::Type::Ssl && ssl_ctx_id.GetId() >= 0 &&
|
|
ssl_ctx_id.GetId() < SSL_MAX && m_ssl[ssl_ctx_id.GetId()].used;
|
|
}
|
|
|
|
Network::Id Network::HttpInit(int memid, Id ssl_ctx_id, uint64_t pool_size) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (ssl_ctx_id.GetType() == Id::Type::Ssl && ssl_ctx_id.GetId() >= 0 &&
|
|
ssl_ctx_id.GetId() < SSL_MAX && m_ssl[ssl_ctx_id.GetId()].used && memid >= 0 &&
|
|
memid < POOLS_MAX && m_pools[memid].used) {
|
|
for (int id = 0; id < HTTP_MAX; id++) {
|
|
if (!m_http[id].used) {
|
|
m_http[id].used = true;
|
|
m_http[id].size = pool_size;
|
|
m_http[id].ssl_ctx_id = ssl_ctx_id.GetId();
|
|
m_http[id].memid = memid;
|
|
|
|
return Id::Create(id, Id::Type::Http);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Id::Invalid();
|
|
}
|
|
|
|
bool Network::HttpValid(Id http_ctx_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
return (http_ctx_id.GetType() == Id::Type::Http && http_ctx_id.GetId() >= 0 &&
|
|
http_ctx_id.GetId() < HTTP_MAX && m_http[http_ctx_id.GetId()].used);
|
|
}
|
|
|
|
bool Network::HttpValidTemplate(Id tmpl_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
return (tmpl_id.GetType() == Id::Type::Template && tmpl_id.GetId() >= 0 &&
|
|
static_cast<size_t>(tmpl_id.GetId()) < m_templates.size() &&
|
|
m_templates[tmpl_id.GetId()].used);
|
|
}
|
|
|
|
bool Network::HttpValidConnection(Id conn_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
return (conn_id.GetType() == Id::Type::Connection && conn_id.GetId() >= 0 &&
|
|
static_cast<size_t>(conn_id.GetId()) < m_connections.size() &&
|
|
m_connections[conn_id.GetId()].used);
|
|
}
|
|
|
|
bool Network::HttpValidRequest(Id req_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
return (req_id.GetType() == Id::Type::Request && req_id.GetId() >= 0 &&
|
|
static_cast<size_t>(req_id.GetId()) < m_requests.size() &&
|
|
m_requests[req_id.GetId()].used);
|
|
}
|
|
|
|
Network::HttpBase* Network::FindHttpBase(Id id, bool include_request) {
|
|
const auto index = id.GetId();
|
|
|
|
switch (id.GetType()) {
|
|
case Id::Type::Template:
|
|
if (index >= 0 && static_cast<size_t>(index) < m_templates.size() &&
|
|
m_templates[index].used) {
|
|
return &m_templates[index];
|
|
}
|
|
break;
|
|
case Id::Type::Connection:
|
|
if (index >= 0 && static_cast<size_t>(index) < m_connections.size() &&
|
|
m_connections[index].used) {
|
|
return &m_connections[index];
|
|
}
|
|
break;
|
|
case Id::Type::Request:
|
|
if (include_request && index >= 0 && static_cast<size_t>(index) < m_requests.size() &&
|
|
m_requests[index].used) {
|
|
return &m_requests[index];
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool Network::HttpTerm(Id http_ctx_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (HttpValid(http_ctx_id)) {
|
|
m_http[http_ctx_id.GetId()].used = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Network::Id Network::HttpCreateTemplate(Id http_ctx_id, const char* user_agent, int http_ver,
|
|
bool is_auto_proxy_conf) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (HttpValid(http_ctx_id)) {
|
|
HttpTemplate tn {};
|
|
tn.used = true;
|
|
tn.http_ver = http_ver;
|
|
tn.user_agent = std::string(user_agent);
|
|
tn.is_auto_proxy_conf = is_auto_proxy_conf;
|
|
tn.http_ctx_id = http_ctx_id.GetId();
|
|
tn.nonblock = false;
|
|
|
|
int index = 0;
|
|
for (auto& t: m_templates) {
|
|
if (!t.used) {
|
|
t = tn;
|
|
return Id::Create(index, Id::Type::Template);
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (index < Id::MAX_ID) {
|
|
m_templates.push_back(tn);
|
|
return Id::Create(index, Id::Type::Template);
|
|
}
|
|
}
|
|
|
|
return Id::Invalid();
|
|
}
|
|
|
|
Network::Id Network::HttpCreateConnectionWithURL(Id tmpl_id, const char* url,
|
|
bool enable_keep_alive) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (url != nullptr && url[0] != '\0' && HttpValidTemplate(tmpl_id)) {
|
|
HttpConnection cn(m_templates[tmpl_id.GetId()]);
|
|
cn.used = true;
|
|
cn.enable_keep_alive = enable_keep_alive;
|
|
cn.url = std::string(url);
|
|
// cn.tmpl_id = tmpl_id.ToInt();
|
|
|
|
int index = 0;
|
|
for (auto& t: m_connections) {
|
|
if (!t.used) {
|
|
t = cn;
|
|
return Id::Create(index, Id::Type::Connection);
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (index < Id::MAX_ID) {
|
|
m_connections.push_back(cn);
|
|
return Id::Create(index, Id::Type::Connection);
|
|
}
|
|
}
|
|
|
|
return Id::Invalid();
|
|
}
|
|
|
|
Network::Id Network::HttpCreateConnection(Id tmpl_id, const char* server_name, const char* scheme,
|
|
uint16_t port, bool enable_keep_alive) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (server_name != nullptr && scheme != nullptr && HttpValidTemplate(tmpl_id)) {
|
|
HttpConnection cn(m_templates[tmpl_id.GetId()]);
|
|
cn.used = true;
|
|
cn.enable_keep_alive = enable_keep_alive;
|
|
cn.url = std::string(scheme) + "://" + std::string(server_name);
|
|
if (port != 0) {
|
|
cn.url = cn.url + fmt::format(":{}", static_cast<unsigned>(port));
|
|
}
|
|
|
|
int index = 0;
|
|
for (auto& t: m_connections) {
|
|
if (!t.used) {
|
|
t = cn;
|
|
return Id::Create(index, Id::Type::Connection);
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (index < Id::MAX_ID) {
|
|
m_connections.push_back(cn);
|
|
return Id::Create(index, Id::Type::Connection);
|
|
}
|
|
}
|
|
|
|
return Id::Invalid();
|
|
}
|
|
|
|
bool Network::HttpDeleteConnection(Id conn_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (HttpValidConnection(conn_id)) {
|
|
m_connections[conn_id.GetId()].used = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Network::Id Network::HttpCreateRequestWithURL2(Id conn_id, const char* method, const char* url,
|
|
uint64_t content_length) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (method != nullptr && url != nullptr && url[0] != '\0' && HttpValidConnection(conn_id)) {
|
|
HttpRequest cn(m_connections[conn_id.GetId()]);
|
|
cn.used = true;
|
|
cn.method = std::string(method);
|
|
cn.url = std::string(url);
|
|
// cn.conn_id = conn_id.ToInt();
|
|
cn.content_length = content_length;
|
|
|
|
int index = 0;
|
|
for (auto& t: m_requests) {
|
|
if (!t.used) {
|
|
t = cn;
|
|
return Id::Create(index, Id::Type::Request);
|
|
}
|
|
index++;
|
|
}
|
|
|
|
if (index < Id::MAX_ID) {
|
|
m_requests.push_back(cn);
|
|
return Id::Create(index, Id::Type::Request);
|
|
}
|
|
}
|
|
|
|
return Id::Invalid();
|
|
}
|
|
|
|
bool Network::HttpDeleteRequest(Id req_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (HttpValidRequest(req_id)) {
|
|
m_requests[req_id.GetId()].used = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetRequestContentLength(Id req_id, uint64_t content_length) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (HttpValidRequest(req_id)) {
|
|
m_requests[req_id.GetId()].content_length = content_length;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpMarkRequestSent(Id req_id, int result) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (req_id.GetType() == Id::Type::Request && req_id.GetId() >= 0 &&
|
|
static_cast<size_t>(req_id.GetId()) < m_requests.size() &&
|
|
m_requests[req_id.GetId()].used) {
|
|
auto& request = m_requests[req_id.GetId()];
|
|
request.send_attempted = true;
|
|
request.send_result = result;
|
|
request.status_code = (result == OK ? 200 : 0);
|
|
request.response_headers.clear();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpGetRequestResponse(Id req_id, int* send_result, int* status_code,
|
|
const char** headers, size_t* headers_size,
|
|
uint64_t* content_length) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (req_id.GetType() != Id::Type::Request || req_id.GetId() < 0 ||
|
|
static_cast<size_t>(req_id.GetId()) >= m_requests.size() ||
|
|
!m_requests[req_id.GetId()].used) {
|
|
return false;
|
|
}
|
|
|
|
const auto& request = m_requests[req_id.GetId()];
|
|
|
|
if (send_result != nullptr) {
|
|
*send_result = (request.send_attempted ? request.send_result : HTTP_ERROR_BEFORE_SEND);
|
|
}
|
|
if (status_code != nullptr) {
|
|
*status_code = request.status_code;
|
|
}
|
|
if (headers != nullptr) {
|
|
*headers = request.response_headers.c_str();
|
|
}
|
|
if (headers_size != nullptr) {
|
|
*headers_size = request.response_headers.size();
|
|
}
|
|
if (content_length != nullptr) {
|
|
*content_length = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Network::HttpDeleteTemplate(Id tmpl_id) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
if (HttpValidTemplate(tmpl_id)) {
|
|
m_templates[tmpl_id.GetId()].used = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetNonblock(Id id, bool enable) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->nonblock = enable;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpsSetSslCallback(Id id, HttpsCallback cbfunc, void* user_arg) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->ssl_cbfunc = cbfunc;
|
|
base->ssl_user_arg = user_arg;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpsSetMinSslVersion(Id id, uint32_t ssl_version) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->min_ssl_version = ssl_version;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpsDisableOption(Id id, uint32_t ssl_flags) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->ssl_flags &= ~ssl_flags;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpAddRequestHeader(Id id, const char* name, const char* value, bool add) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
HttpHeader nh({std::string(name), std::string(value)});
|
|
if (add) {
|
|
base->headers.push_back(nh);
|
|
} else {
|
|
for (auto& h: base->headers) {
|
|
if (h.name == nh.name) {
|
|
h.value = nh.value;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetResolveTimeOut(Id id, uint32_t usec) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, false);
|
|
|
|
if (base != nullptr) {
|
|
base->resolve_timeout = usec;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetResolveRetry(Id id, int32_t retry) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, false);
|
|
|
|
if (base != nullptr) {
|
|
base->resolve_retry = retry;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetConnectTimeOut(Id id, uint32_t usec) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->connect_timeout = usec;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetSendTimeOut(Id id, uint32_t usec) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->send_timeout = usec;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetRecvTimeOut(Id id, uint32_t usec) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->recv_timeout = usec;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetAutoRedirect(Id id, int enable) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->auto_redirect = (enable != 0);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Network::HttpSetAuthEnabled(Id id, int enable) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
HttpBase* base = FindHttpBase(id, true);
|
|
|
|
if (base != nullptr) {
|
|
base->auth_enabled = (enable != 0);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
namespace Net {
|
|
|
|
LIB_NAME("Net", "Net");
|
|
|
|
struct NetEtherAddr {
|
|
uint8_t data[6] = {0};
|
|
};
|
|
|
|
#if defined(_WIN32)
|
|
using NativeSocket = SOCKET;
|
|
static constexpr NativeSocket INVALID_NATIVE_SOCKET = INVALID_SOCKET;
|
|
using SocketLength = int;
|
|
using SocketIoLength = int;
|
|
#else
|
|
using NativeSocket = int;
|
|
static constexpr NativeSocket INVALID_NATIVE_SOCKET = -1;
|
|
using SocketLength = socklen_t;
|
|
using SocketIoLength = size_t;
|
|
#endif
|
|
|
|
struct SocketTransport {
|
|
explicit SocketTransport(NativeSocket value): socket(value) {}
|
|
~SocketTransport() { Close(); }
|
|
int Close() {
|
|
if (socket == INVALID_NATIVE_SOCKET) {
|
|
return 0;
|
|
}
|
|
const auto value = std::exchange(socket, INVALID_NATIVE_SOCKET);
|
|
#if defined(_WIN32)
|
|
return closesocket(value);
|
|
#else
|
|
return ::close(value);
|
|
#endif
|
|
}
|
|
NativeSocket socket;
|
|
};
|
|
|
|
struct P2pEndpoint {
|
|
bool bound = false;
|
|
uint32_t address = 0;
|
|
uint16_t port = 0;
|
|
uint16_t virtual_port = 0;
|
|
int nonblocking = 0;
|
|
int broadcast = 0;
|
|
int send_buffer = 9216;
|
|
int receive_buffer = 40 * 1024;
|
|
};
|
|
|
|
struct SocketSlot {
|
|
std::shared_ptr<SocketTransport> transport;
|
|
std::optional<P2pEndpoint> p2p;
|
|
};
|
|
|
|
struct NetTimeval {
|
|
int64_t tv_sec;
|
|
int64_t tv_usec;
|
|
};
|
|
|
|
static constexpr int SOCKET_FD_MIN = 128;
|
|
static constexpr int SOCKET_FD_MAX = 1024;
|
|
static Common::Mutex g_socket_mutex;
|
|
static std::array<SocketSlot, SOCKET_FD_MAX> g_sockets;
|
|
static std::atomic_bool g_winsock_initialized = false;
|
|
|
|
constexpr int EPOLL_ID_BASE = SOCKET_FD_MAX;
|
|
constexpr uint32_t EPOLL_IN = 0x00000001;
|
|
constexpr uint32_t EPOLL_OUT = 0x00000002;
|
|
constexpr uint32_t EPOLL_ERR = 0x00000008;
|
|
|
|
struct EpollRegistration {
|
|
int id = -1;
|
|
NetEpollEvent event {};
|
|
};
|
|
|
|
struct EpollSlot {
|
|
bool used = false;
|
|
std::string name;
|
|
std::vector<EpollRegistration> registrations;
|
|
};
|
|
|
|
static std::mutex g_epoll_mutex;
|
|
static std::condition_variable g_epoll_changed;
|
|
static std::array<EpollSlot, 32> g_epolls;
|
|
|
|
static EpollSlot* GetEpollSlot(int eid) {
|
|
const auto index = eid - EPOLL_ID_BASE;
|
|
if (index < 0 || index >= static_cast<int>(g_epolls.size())) {
|
|
return nullptr;
|
|
}
|
|
auto& slot = g_epolls[static_cast<size_t>(index)];
|
|
return slot.used ? &slot : nullptr;
|
|
}
|
|
|
|
static void RemoveSocketFromEpolls(int id) {
|
|
std::lock_guard lock(g_epoll_mutex);
|
|
for (auto& epoll: g_epolls) {
|
|
if (!epoll.used) {
|
|
continue;
|
|
}
|
|
std::erase_if(epoll.registrations,
|
|
[id](const auto& registration) { return registration.id == id; });
|
|
}
|
|
g_epoll_changed.notify_all();
|
|
}
|
|
|
|
static bool EnsureSocketBackend() {
|
|
#if defined(_WIN32)
|
|
if (!g_winsock_initialized.load()) {
|
|
WSADATA data {};
|
|
if (WSAStartup(MAKEWORD(2, 2), &data) != 0) {
|
|
return false;
|
|
}
|
|
g_winsock_initialized = true;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
static int SetGuestSocketError(int error) {
|
|
*Posix::GetErrorAddr() = error;
|
|
return -1;
|
|
}
|
|
|
|
static int ConvertHostSocketError(int error) {
|
|
if (error == 0) {
|
|
return 0;
|
|
}
|
|
int posix_error = Posix::POSIX_EIO;
|
|
#if defined(_WIN32)
|
|
switch (error) {
|
|
case WSAEACCES: posix_error = Posix::POSIX_EACCES; break;
|
|
case WSAEADDRINUSE: posix_error = Posix::POSIX_EADDRINUSE; break;
|
|
case WSAEADDRNOTAVAIL: posix_error = Posix::POSIX_EADDRNOTAVAIL; break;
|
|
case WSAEAFNOSUPPORT: posix_error = Posix::POSIX_EAFNOSUPPORT; break;
|
|
case WSAEFAULT: posix_error = Posix::POSIX_EFAULT; break;
|
|
case WSAEINTR: posix_error = Posix::POSIX_EINTR; break;
|
|
case WSAEINVAL: posix_error = Posix::POSIX_EINVAL; break;
|
|
case WSAEISCONN: posix_error = Posix::POSIX_EISCONN; break;
|
|
case WSAEMFILE: posix_error = Posix::POSIX_EMFILE; break;
|
|
case WSAEMSGSIZE: posix_error = Posix::POSIX_EMSGSIZE; break;
|
|
case WSAENOBUFS: posix_error = Posix::POSIX_ENOBUFS; break;
|
|
case WSAENETDOWN: posix_error = Posix::POSIX_ENETDOWN; break;
|
|
case WSAENETRESET: posix_error = Posix::POSIX_ENETRESET; break;
|
|
case WSAENETUNREACH: posix_error = Posix::POSIX_ENETUNREACH; break;
|
|
case WSAENOTCONN: posix_error = Posix::POSIX_ENOTCONN; break;
|
|
case WSAENOTSOCK: posix_error = Posix::POSIX_ENOTSOCK; break;
|
|
case WSAEOPNOTSUPP: posix_error = Posix::POSIX_EOPNOTSUPP; break;
|
|
case WSAEPROTONOSUPPORT: posix_error = Posix::POSIX_EPROTONOSUPPORT; break;
|
|
case WSAESHUTDOWN: posix_error = Posix::POSIX_ESHUTDOWN; break;
|
|
case WSAETIMEDOUT: posix_error = Posix::POSIX_ETIMEDOUT; break;
|
|
case WSAEWOULDBLOCK: posix_error = Posix::POSIX_EWOULDBLOCK; break;
|
|
case WSAECONNABORTED: posix_error = Posix::POSIX_ECONNABORTED; break;
|
|
case WSAECONNREFUSED: posix_error = Posix::POSIX_ECONNREFUSED; break;
|
|
case WSAECONNRESET: posix_error = Posix::POSIX_ECONNRESET; break;
|
|
case WSAEDESTADDRREQ: posix_error = Posix::POSIX_EDESTADDRREQ; break;
|
|
case WSAEHOSTUNREACH: posix_error = Posix::POSIX_EHOSTUNREACH; break;
|
|
case WSAEINPROGRESS: posix_error = Posix::POSIX_EINPROGRESS; break;
|
|
case WSAEALREADY: posix_error = Posix::POSIX_EALREADY; break;
|
|
default: break;
|
|
}
|
|
#else
|
|
switch (error) {
|
|
case EACCES: posix_error = Posix::POSIX_EACCES; break;
|
|
case EADDRINUSE: posix_error = Posix::POSIX_EADDRINUSE; break;
|
|
case EADDRNOTAVAIL: posix_error = Posix::POSIX_EADDRNOTAVAIL; break;
|
|
case EAFNOSUPPORT: posix_error = Posix::POSIX_EAFNOSUPPORT; break;
|
|
case EAGAIN: posix_error = Posix::POSIX_EWOULDBLOCK; break;
|
|
case EBADF: posix_error = Posix::POSIX_EBADF; break;
|
|
case EFAULT: posix_error = Posix::POSIX_EFAULT; break;
|
|
case EINVAL: posix_error = Posix::POSIX_EINVAL; break;
|
|
case EMFILE: posix_error = Posix::POSIX_EMFILE; break;
|
|
case ENFILE: posix_error = Posix::POSIX_ENFILE; break;
|
|
case ENOBUFS: posix_error = Posix::POSIX_ENOBUFS; break;
|
|
case ENOMEM: posix_error = Posix::POSIX_ENOMEM; break;
|
|
case ENOTSOCK: posix_error = Posix::POSIX_ENOTSOCK; break;
|
|
case EPROTONOSUPPORT: posix_error = Posix::POSIX_EPROTONOSUPPORT; break;
|
|
default: break;
|
|
}
|
|
#endif
|
|
return posix_error;
|
|
}
|
|
|
|
static int SetHostSocketError() {
|
|
#if defined(_WIN32)
|
|
return SetGuestSocketError(ConvertHostSocketError(WSAGetLastError()));
|
|
#else
|
|
return SetGuestSocketError(ConvertHostSocketError(errno));
|
|
#endif
|
|
}
|
|
|
|
static int ConvertFamily(int family) {
|
|
switch (family) {
|
|
case 2: return AF_INET;
|
|
case 28: return AF_INET6;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
static int ConvertSocketOptionLevel(int level) {
|
|
return (level == 0xffff ? SOL_SOCKET : level);
|
|
}
|
|
|
|
static int* P2pSocketOption(P2pEndpoint& endpoint, int option) {
|
|
switch (option) {
|
|
case 0x0020: return &endpoint.broadcast;
|
|
case 0x1001: return &endpoint.send_buffer;
|
|
case 0x1002: return &endpoint.receive_buffer;
|
|
case 0x1200: return &endpoint.nonblocking;
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
static int ConvertMessageFlags(int flags) {
|
|
constexpr int guest_msg_peek = 0x00000002;
|
|
constexpr int guest_msg_dontroute = 0x00000004;
|
|
constexpr int guest_msg_waitall = 0x00000040;
|
|
constexpr int guest_msg_dontwait = 0x00000080;
|
|
constexpr int guest_msg_nosignal = 0x00020000;
|
|
|
|
int host_flags = 0;
|
|
if ((flags & guest_msg_peek) != 0) {
|
|
host_flags |= MSG_PEEK;
|
|
}
|
|
if ((flags & guest_msg_dontroute) != 0) {
|
|
host_flags |= MSG_DONTROUTE;
|
|
}
|
|
if ((flags & guest_msg_waitall) != 0) {
|
|
host_flags |= MSG_WAITALL;
|
|
}
|
|
#if !defined(_WIN32)
|
|
if ((flags & guest_msg_dontwait) != 0) {
|
|
host_flags |= MSG_DONTWAIT;
|
|
}
|
|
if ((flags & guest_msg_nosignal) != 0) {
|
|
host_flags |= MSG_NOSIGNAL;
|
|
}
|
|
#endif
|
|
|
|
flags &= ~(guest_msg_peek | guest_msg_dontroute | guest_msg_waitall | guest_msg_dontwait |
|
|
guest_msg_nosignal);
|
|
if (flags != 0) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EOPNOTSUPP;
|
|
return -1;
|
|
}
|
|
|
|
return host_flags;
|
|
}
|
|
|
|
static int ConvertGuestSockaddr(const void* addr, uint32_t addrlen, sockaddr_storage* out,
|
|
SocketLength* out_len) {
|
|
EXIT_IF(out == nullptr);
|
|
EXIT_IF(out_len == nullptr);
|
|
|
|
if (addr == nullptr) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EFAULT;
|
|
return -1;
|
|
}
|
|
|
|
const auto* bytes = static_cast<const uint8_t*>(addr);
|
|
if (addrlen < 2) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
const int family = ConvertFamily(bytes[1]);
|
|
if (family != AF_INET) {
|
|
*Posix::GetErrorAddr() = (family < 0 ? Posix::POSIX_EAFNOSUPPORT : Posix::POSIX_EOPNOTSUPP);
|
|
return -1;
|
|
}
|
|
|
|
if (addrlen < 8) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
std::memset(out, 0, sizeof(*out));
|
|
auto* in = reinterpret_cast<sockaddr_in*>(out);
|
|
in->sin_family = AF_INET;
|
|
std::memcpy(&in->sin_port, bytes + 2, sizeof(in->sin_port));
|
|
std::memcpy(&in->sin_addr, bytes + 4, sizeof(in->sin_addr));
|
|
*out_len = sizeof(sockaddr_in);
|
|
return 0;
|
|
}
|
|
|
|
static int ConvertHostSockaddr(const sockaddr_storage* addr, SocketLength addrlen, void* out,
|
|
uint32_t* out_len) {
|
|
EXIT_IF(addr == nullptr);
|
|
EXIT_IF(out_len == nullptr);
|
|
|
|
if (out == nullptr) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EFAULT;
|
|
return -1;
|
|
}
|
|
|
|
if (addr->ss_family != AF_INET ||
|
|
addrlen < static_cast<SocketLength>(sizeof(sockaddr_in))) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EOPNOTSUPP;
|
|
return -1;
|
|
}
|
|
|
|
static constexpr uint32_t guest_addrlen = 16;
|
|
if (*out_len < guest_addrlen) {
|
|
*out_len = guest_addrlen;
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
auto* bytes = static_cast<uint8_t*>(out);
|
|
const auto* in = reinterpret_cast<const sockaddr_in*>(addr);
|
|
std::memset(bytes, 0, guest_addrlen);
|
|
bytes[0] = static_cast<uint8_t>(guest_addrlen);
|
|
bytes[1] = 2;
|
|
std::memcpy(bytes + 2, &in->sin_port, sizeof(in->sin_port));
|
|
std::memcpy(bytes + 4, &in->sin_addr, sizeof(in->sin_addr));
|
|
*out_len = guest_addrlen;
|
|
return 0;
|
|
}
|
|
|
|
static bool GetSocketBackend(int guest_fd, NativeSocket* out, SocketSlot* state = nullptr) {
|
|
EXIT_IF(out == nullptr);
|
|
|
|
if (guest_fd < 0 || guest_fd >= SOCKET_FD_MAX) {
|
|
SetGuestSocketError(Posix::POSIX_EBADF);
|
|
return false;
|
|
}
|
|
|
|
Common::LockGuard lock(g_socket_mutex);
|
|
const auto& slot = g_sockets[static_cast<size_t>(guest_fd)];
|
|
if (!slot.transport) {
|
|
SetGuestSocketError(Posix::POSIX_EBADF);
|
|
return false;
|
|
}
|
|
// Native-only operations cannot act on a transport shared by virtual ports.
|
|
if (slot.p2p && state == nullptr) {
|
|
SetGuestSocketError(Posix::POSIX_EOPNOTSUPP);
|
|
return false;
|
|
}
|
|
|
|
*out = slot.transport->socket;
|
|
if (state != nullptr) {
|
|
*state = slot;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool KYTY_SYSV_ABI IsSocket(int s) {
|
|
if (s < 0 || s >= SOCKET_FD_MAX) {
|
|
return false;
|
|
}
|
|
|
|
Common::LockGuard lock(g_socket_mutex);
|
|
return g_sockets[static_cast<size_t>(s)].transport != nullptr;
|
|
}
|
|
|
|
static bool TakeSocketBackend(int guest_fd, SocketSlot* out) {
|
|
EXIT_IF(out == nullptr);
|
|
|
|
if (guest_fd < 0 || guest_fd >= SOCKET_FD_MAX) {
|
|
return false;
|
|
}
|
|
|
|
Common::LockGuard lock(g_socket_mutex);
|
|
auto& slot = g_sockets[static_cast<size_t>(guest_fd)];
|
|
if (!slot.transport) {
|
|
return false;
|
|
}
|
|
|
|
*out = std::move(slot);
|
|
slot = {};
|
|
return true;
|
|
}
|
|
|
|
static int AllocSocketFd(const std::shared_ptr<SocketTransport>& transport, bool p2p = false) {
|
|
Common::LockGuard lock(g_socket_mutex);
|
|
for (int fd = SOCKET_FD_MIN; fd < SOCKET_FD_MAX; fd++) {
|
|
auto& slot = g_sockets[static_cast<size_t>(fd)];
|
|
if (!slot.transport) {
|
|
slot.transport = transport;
|
|
if (p2p) {
|
|
slot.p2p.emplace();
|
|
}
|
|
return fd;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static bool GuestFdIsSet(const void* fds, int fd) {
|
|
if (fds == nullptr || fd < 0 || fd >= SOCKET_FD_MAX) {
|
|
return false;
|
|
}
|
|
|
|
const auto* words = static_cast<const uint64_t*>(fds);
|
|
return (words[fd / 64] & (uint64_t {1} << (fd % 64))) != 0;
|
|
}
|
|
|
|
static void GuestFdZero(void* fds, int nfds) {
|
|
if (fds == nullptr || nfds <= 0) {
|
|
return;
|
|
}
|
|
|
|
std::memset(fds, 0, static_cast<size_t>((nfds + 63) / 64) * sizeof(uint64_t));
|
|
}
|
|
|
|
static void GuestFdSet(void* fds, int fd) {
|
|
if (fds == nullptr || fd < 0 || fd >= SOCKET_FD_MAX) {
|
|
return;
|
|
}
|
|
|
|
auto* words = static_cast<uint64_t*>(fds);
|
|
words[fd / 64] |= (uint64_t {1} << (fd % 64));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetInit() {
|
|
PRINT_NAME();
|
|
|
|
EnsureSocketBackend();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetTerm() {
|
|
PRINT_NAME();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetPoolCreate(const char* name, int size, int flags) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t name = %s\n"
|
|
"\t size = %d\n"
|
|
"\t flags = %d\n",
|
|
name, size, flags);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(flags != 0);
|
|
EXIT_NOT_IMPLEMENTED(size == 0);
|
|
|
|
int id = g_net->PoolCreate(name, size);
|
|
|
|
if (id < 0) {
|
|
return NET_ERROR_ENFILE;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetPoolDestroy(int memid) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->PoolDestroy(memid)) {
|
|
return NET_ERROR_EBADF;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetResolverCreate(const char* name, int memid, int flags) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t name = %s\n"
|
|
"\t memid = %d\n"
|
|
"\t flags = %d\n",
|
|
name != nullptr ? name : "<null>", memid, flags);
|
|
|
|
if (name == nullptr || flags != 0) {
|
|
return NET_ERROR_EINVAL;
|
|
}
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
const int id = g_net->ResolverCreate(name, memid);
|
|
if (id < 0) {
|
|
return NET_ERROR_RESOLVER_ENOSPACE;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetResolverDestroy(int rid) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t rid = %d\n", rid);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
if (!g_net->ResolverDestroy(rid)) {
|
|
return NET_ERROR_EBADF;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetResolverStartNtoa(int rid, const char* hostname, void* addr, int timeout,
|
|
int retry, int flags) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t rid = %d\n"
|
|
"\t hostname = %s\n"
|
|
"\t timeout = %d\n"
|
|
"\t retry = %d\n"
|
|
"\t flags = 0x%08x\n",
|
|
rid, hostname != nullptr ? hostname : "<null>", timeout, retry, flags);
|
|
|
|
(void)timeout;
|
|
(void)retry;
|
|
|
|
if (hostname == nullptr || addr == nullptr || (flags & ~0x00010000) != 0) {
|
|
return NET_ERROR_EINVAL;
|
|
}
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
if (!g_net->ResolverValid(rid)) {
|
|
return NET_ERROR_EBADF;
|
|
}
|
|
|
|
if ((flags & 0x00010000) == 0 && NetInetPton(2, hostname, addr) == 1) {
|
|
return OK;
|
|
}
|
|
|
|
if (!EnsureSocketBackend()) {
|
|
return NET_ERROR_ENETDOWN;
|
|
}
|
|
|
|
addrinfo hints {};
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
addrinfo* result = nullptr;
|
|
const int ret = getaddrinfo(hostname, nullptr, &hints, &result);
|
|
if (ret != 0 || result == nullptr) {
|
|
if (result != nullptr) {
|
|
freeaddrinfo(result);
|
|
}
|
|
if (ret == EAI_NONAME) {
|
|
return NET_ERROR_RESOLVER_ENOHOST;
|
|
}
|
|
if (ret == EAI_NODATA) {
|
|
return NET_ERROR_RESOLVER_ENORECORD;
|
|
}
|
|
return NET_ERROR_RESOLVER_EINTERNAL;
|
|
}
|
|
|
|
for (auto* ai = result; ai != nullptr; ai = ai->ai_next) {
|
|
if (ai->ai_family == AF_INET && ai->ai_addr != nullptr &&
|
|
ai->ai_addrlen >= sizeof(sockaddr_in)) {
|
|
const auto* in = reinterpret_cast<const sockaddr_in*>(ai->ai_addr);
|
|
std::memcpy(addr, &in->sin_addr, sizeof(in->sin_addr));
|
|
freeaddrinfo(result);
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
return NET_ERROR_RESOLVER_ENORECORD;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetInetPton(int af, const char* src, void* dst) {
|
|
PRINT_NAME();
|
|
|
|
if (src == nullptr || dst == nullptr) {
|
|
return NET_ERROR_EINVAL;
|
|
}
|
|
|
|
const int host_family = ConvertFamily(af);
|
|
if (host_family < 0) {
|
|
return NET_ERROR_EAFNOSUPPORT;
|
|
}
|
|
|
|
LOGF("\t src = %.46s\n", src);
|
|
|
|
#if defined(_WIN32)
|
|
const int result = ::InetPtonA(host_family, src, dst);
|
|
#else
|
|
const int result = ::inet_pton(host_family, src, dst);
|
|
#endif
|
|
return result < 0 ? NET_ERROR_EINVAL : result;
|
|
}
|
|
|
|
const char* KYTY_SYSV_ABI NetInetNtop(int af, const void* src, char* dst, uint32_t size) {
|
|
PRINT_NAME();
|
|
|
|
if (src == nullptr || dst == nullptr || size == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (af != 2 && af != 28) {
|
|
return nullptr;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
const int win_af = (af == 28 ? AF_INET6 : AF_INET);
|
|
if (::InetNtopA(win_af, const_cast<void*>(src), dst, size) == nullptr) {
|
|
return nullptr;
|
|
}
|
|
#else
|
|
if (::inet_ntop(af, src, dst, size) == nullptr) {
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
return dst;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetEtherNtostr(const NetEtherAddr* n, char* str, size_t len) {
|
|
PRINT_NAME();
|
|
|
|
NetEtherAddr zero {};
|
|
|
|
EXIT_NOT_IMPLEMENTED(len != 18);
|
|
EXIT_NOT_IMPLEMENTED(n == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(str == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(memcmp(n->data, zero.data, sizeof(zero.data)) != 0);
|
|
|
|
strcpy(str, "00:00:00:00:00:00"); // NOLINT
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetGetMacAddress(NetEtherAddr* addr, int flags) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(addr == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(flags != 0);
|
|
|
|
memset(addr->data, 0, sizeof(addr->data));
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetGetSockInfo(int s, void* info, int n, int flags) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t info = 0x%016" PRIx64 "\n"
|
|
"\t n = %d\n"
|
|
"\t flags = %d\n",
|
|
s, reinterpret_cast<uint64_t>(info), n, flags);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI EpollCreate(const char* name, int flags) {
|
|
if (name == nullptr || flags != 0) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
|
|
std::lock_guard lock(g_epoll_mutex);
|
|
for (size_t index = 0; index < g_epolls.size(); index++) {
|
|
auto& slot = g_epolls[index];
|
|
if (!slot.used) {
|
|
slot.used = true;
|
|
slot.name = name;
|
|
slot.registrations.clear();
|
|
return EPOLL_ID_BASE + static_cast<int>(index);
|
|
}
|
|
}
|
|
|
|
return SetGuestSocketError(Posix::POSIX_EMFILE);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI EpollControl(int eid, int op, int id, const NetEpollEvent* event) {
|
|
constexpr int EPOLL_CTL_ADD = 1;
|
|
constexpr int EPOLL_CTL_MOD = 2;
|
|
constexpr int EPOLL_CTL_DEL = 3;
|
|
|
|
if ((op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD) && event == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
if (op == EPOLL_CTL_DEL && event != nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
if (op < EPOLL_CTL_ADD || op > EPOLL_CTL_DEL) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
if (!IsSocket(id)) {
|
|
return SetGuestSocketError(Posix::POSIX_EBADF);
|
|
}
|
|
|
|
std::lock_guard lock(g_epoll_mutex);
|
|
auto* slot = GetEpollSlot(eid);
|
|
if (slot == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EBADF);
|
|
}
|
|
|
|
auto registration = std::find_if(slot->registrations.begin(), slot->registrations.end(),
|
|
[id](const auto& value) { return value.id == id; });
|
|
switch (op) {
|
|
case EPOLL_CTL_ADD:
|
|
if (registration != slot->registrations.end()) {
|
|
return SetGuestSocketError(Posix::POSIX_EEXIST);
|
|
}
|
|
slot->registrations.push_back({id, *event});
|
|
break;
|
|
case EPOLL_CTL_MOD:
|
|
if (registration == slot->registrations.end()) {
|
|
return SetGuestSocketError(Posix::POSIX_ENOENT);
|
|
}
|
|
registration->event = *event;
|
|
break;
|
|
case EPOLL_CTL_DEL:
|
|
if (registration == slot->registrations.end()) {
|
|
return SetGuestSocketError(Posix::POSIX_ENOENT);
|
|
}
|
|
slot->registrations.erase(registration);
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
g_epoll_changed.notify_all();
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI EpollWait(int eid, NetEpollEvent* events, int maxevents, int timeout) {
|
|
if (events == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EFAULT);
|
|
}
|
|
if (maxevents <= 0 || timeout < -1) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
|
|
std::vector<EpollRegistration> registrations;
|
|
{
|
|
std::unique_lock lock(g_epoll_mutex);
|
|
auto* slot = GetEpollSlot(eid);
|
|
if (slot == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EBADF);
|
|
}
|
|
|
|
if (slot->registrations.empty() && timeout != 0) {
|
|
auto ready = [eid] {
|
|
auto* current = GetEpollSlot(eid);
|
|
return current == nullptr || !current->registrations.empty();
|
|
};
|
|
if (timeout < 0) {
|
|
g_epoll_changed.wait(lock, ready);
|
|
} else {
|
|
g_epoll_changed.wait_for(lock, std::chrono::microseconds(timeout), ready);
|
|
}
|
|
slot = GetEpollSlot(eid);
|
|
if (slot == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EBADF);
|
|
}
|
|
}
|
|
registrations = slot->registrations;
|
|
}
|
|
|
|
if (registrations.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
struct HostRegistration {
|
|
EpollRegistration guest;
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
SocketSlot state;
|
|
};
|
|
|
|
fd_set host_read {};
|
|
fd_set host_write {};
|
|
fd_set host_except {};
|
|
FD_ZERO(&host_read);
|
|
FD_ZERO(&host_write);
|
|
FD_ZERO(&host_except);
|
|
|
|
std::vector<HostRegistration> host_registrations;
|
|
host_registrations.reserve(registrations.size());
|
|
int host_nfds = 0;
|
|
for (const auto& registration: registrations) {
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
SocketSlot state;
|
|
if (!GetSocketBackend(registration.id, &socket, &state)) {
|
|
continue;
|
|
}
|
|
#if defined(_WIN32)
|
|
if (host_registrations.size() >= FD_SETSIZE) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
#else
|
|
if (socket >= FD_SETSIZE) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
host_nfds = std::max(host_nfds, socket + 1);
|
|
#endif
|
|
if ((registration.event.events & EPOLL_IN) != 0) {
|
|
FD_SET(socket, &host_read);
|
|
}
|
|
if ((registration.event.events & EPOLL_OUT) != 0) {
|
|
FD_SET(socket, &host_write);
|
|
}
|
|
FD_SET(socket, &host_except);
|
|
host_registrations.push_back({registration, socket, std::move(state)});
|
|
}
|
|
|
|
if (host_registrations.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
timeval host_timeout {};
|
|
timeval* host_timeout_ptr = nullptr;
|
|
if (timeout >= 0) {
|
|
host_timeout.tv_sec = timeout / 1000000;
|
|
host_timeout.tv_usec = timeout % 1000000;
|
|
host_timeout_ptr = &host_timeout;
|
|
}
|
|
|
|
const int result = ::select(host_nfds, &host_read, &host_write, &host_except, host_timeout_ptr);
|
|
if (result < 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
if (result == 0) {
|
|
return 0;
|
|
}
|
|
|
|
int count = 0;
|
|
for (const auto& registration: host_registrations) {
|
|
uint32_t ready = 0;
|
|
if ((registration.guest.event.events & EPOLL_IN) != 0 &&
|
|
FD_ISSET(registration.socket, &host_read)) {
|
|
if (registration.state.p2p) {
|
|
return SetGuestSocketError(Posix::POSIX_EOPNOTSUPP);
|
|
}
|
|
ready |= EPOLL_IN;
|
|
}
|
|
if ((registration.guest.event.events & EPOLL_OUT) != 0 &&
|
|
FD_ISSET(registration.socket, &host_write)) {
|
|
ready |= EPOLL_OUT;
|
|
}
|
|
if (FD_ISSET(registration.socket, &host_except)) {
|
|
ready |= EPOLL_ERR;
|
|
}
|
|
if (ready == 0) {
|
|
continue;
|
|
}
|
|
|
|
auto& output = events[count++];
|
|
output = registration.guest.event;
|
|
output.events = ready;
|
|
output.reserved = 0;
|
|
output.ident = static_cast<uint64_t>(registration.guest.id);
|
|
if (count == maxevents) {
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI EpollDestroy(int eid) {
|
|
std::lock_guard lock(g_epoll_mutex);
|
|
auto* slot = GetEpollSlot(eid);
|
|
if (slot == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EBADF);
|
|
}
|
|
|
|
slot->used = false;
|
|
slot->name.clear();
|
|
slot->registrations.clear();
|
|
g_epoll_changed.notify_all();
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI SocketClose(int s) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n", s);
|
|
|
|
SocketSlot slot;
|
|
if (!TakeSocketBackend(s, &slot)) {
|
|
return NET_ERROR_EBADF;
|
|
}
|
|
RemoveSocketFromEpolls(s);
|
|
|
|
if (slot.transport.use_count() == 1 && slot.transport->Close() != 0) {
|
|
return NET_ERROR_EBADF;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Socket(int family, int type, int protocol) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t family = %d\n"
|
|
"\t type = %d\n"
|
|
"\t protocol = %d\n",
|
|
family, type, protocol);
|
|
|
|
if (!EnsureSocketBackend()) {
|
|
return SetGuestSocketError(Posix::POSIX_ENETDOWN);
|
|
}
|
|
|
|
const int host_family = ConvertFamily(family);
|
|
if (host_family < 0) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EAFNOSUPPORT;
|
|
return -1;
|
|
}
|
|
|
|
constexpr int socket_dgram_p2p = 6;
|
|
const bool p2p = type == socket_dgram_p2p;
|
|
if (p2p && (family != 2 || protocol != 0)) {
|
|
return SetGuestSocketError(Posix::POSIX_EPROTONOSUPPORT);
|
|
}
|
|
NativeSocket socket = ::socket(host_family, p2p ? SOCK_DGRAM : type, protocol);
|
|
if (socket == INVALID_NATIVE_SOCKET) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
auto transport = std::make_shared<SocketTransport>(socket);
|
|
if (p2p) {
|
|
// Logical sockets keep their own blocking mode; the shared transport never blocks.
|
|
#if defined(_WIN32)
|
|
u_long enabled = 1;
|
|
const int result = ioctlsocket(socket, FIONBIO, &enabled);
|
|
#else
|
|
const int result = ::fcntl(socket, F_SETFL, O_NONBLOCK);
|
|
#endif
|
|
if (result != 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
}
|
|
const int fd = AllocSocketFd(transport, p2p);
|
|
if (fd < 0) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EMFILE;
|
|
return -1;
|
|
}
|
|
|
|
LOGF("\t fd = %d\n", fd);
|
|
return fd;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Bind(int s, const void* addr, uint32_t addrlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t addr = 0x%016" PRIx64 "\n"
|
|
"\t addrlen = %" PRIu32 "\n",
|
|
s, reinterpret_cast<uint64_t>(addr), addrlen);
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
SocketSlot state;
|
|
if (addr == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EFAULT);
|
|
}
|
|
if (!GetSocketBackend(s, &socket, &state)) {
|
|
return -1;
|
|
}
|
|
|
|
sockaddr_storage host_addr {};
|
|
SocketLength host_addrlen = 0;
|
|
if (ConvertGuestSockaddr(addr, addrlen, &host_addr, &host_addrlen) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (state.p2p) {
|
|
if (addrlen < 16) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
const auto& in = *reinterpret_cast<const sockaddr_in*>(&host_addr);
|
|
uint32_t address = 0;
|
|
std::memcpy(&address, &in.sin_addr, sizeof(address));
|
|
uint16_t virtual_port = 0;
|
|
std::memcpy(&virtual_port, static_cast<const uint8_t*>(addr) + 8, sizeof(virtual_port));
|
|
const auto requested_port = ntohs(virtual_port);
|
|
if (requested_port == 0) {
|
|
return SetGuestSocketError(Posix::POSIX_EOPNOTSUPP);
|
|
}
|
|
if (ntohs(in.sin_port) != 3658) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
if (requested_port == 5353 || requested_port >= 32768) {
|
|
return SetGuestSocketError(Posix::POSIX_EACCES);
|
|
}
|
|
|
|
Common::LockGuard lock(g_socket_mutex);
|
|
auto& slot = g_sockets[static_cast<size_t>(s)];
|
|
if (slot.transport != state.transport) {
|
|
return SetGuestSocketError(Posix::POSIX_EBADF);
|
|
}
|
|
auto& endpoint = *slot.p2p;
|
|
if (endpoint.bound) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
auto conflicts = [&](uint16_t port) {
|
|
return std::any_of(g_sockets.begin(), g_sockets.end(), [&](const auto& other) {
|
|
return other.p2p && other.p2p->bound && other.p2p->port == in.sin_port &&
|
|
other.p2p->address == address && other.p2p->virtual_port == port;
|
|
});
|
|
};
|
|
if (conflicts(virtual_port)) {
|
|
return SetGuestSocketError(Posix::POSIX_EADDRINUSE);
|
|
}
|
|
std::shared_ptr<SocketTransport> shared;
|
|
for (const auto& other: g_sockets) {
|
|
if (other.p2p && other.p2p->bound && other.p2p->port == in.sin_port &&
|
|
other.p2p->address == address) {
|
|
shared = other.transport;
|
|
break;
|
|
}
|
|
}
|
|
if (shared) {
|
|
slot.transport = std::move(shared);
|
|
} else if (::bind(socket, reinterpret_cast<const sockaddr*>(&host_addr), host_addrlen) !=
|
|
0) {
|
|
return SetHostSocketError();
|
|
}
|
|
endpoint.bound = true;
|
|
endpoint.address = address;
|
|
endpoint.port = in.sin_port;
|
|
endpoint.virtual_port = virtual_port;
|
|
LOGF("\t UDPP2P bound UDP port=%u virtual port=%u\n", ntohs(endpoint.port),
|
|
ntohs(endpoint.virtual_port));
|
|
return 0;
|
|
}
|
|
|
|
if (::bind(socket, reinterpret_cast<const sockaddr*>(&host_addr), host_addrlen) != 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Connect(int s, const void* addr, uint32_t addrlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t addr = 0x%016" PRIx64 "\n"
|
|
"\t addrlen = %" PRIu32 "\n",
|
|
s, reinterpret_cast<uint64_t>(addr), addrlen);
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
if (addr == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EFAULT);
|
|
}
|
|
if (!GetSocketBackend(s, &socket)) {
|
|
return -1;
|
|
}
|
|
|
|
sockaddr_storage host_addr {};
|
|
SocketLength host_addrlen = 0;
|
|
if (ConvertGuestSockaddr(addr, addrlen, &host_addr, &host_addrlen) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (::connect(socket, reinterpret_cast<const sockaddr*>(&host_addr), host_addrlen) != 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Listen(int s, int backlog) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t backlog = %d\n",
|
|
s, backlog);
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
if (!GetSocketBackend(s, &socket)) {
|
|
return -1;
|
|
}
|
|
|
|
if (::listen(socket, backlog) != 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Accept(int s, void* addr, uint32_t* addrlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t addr = 0x%016" PRIx64 "\n"
|
|
"\t addrlen = 0x%016" PRIx64 "\n",
|
|
s, reinterpret_cast<uint64_t>(addr), reinterpret_cast<uint64_t>(addrlen));
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
if (!GetSocketBackend(s, &socket)) {
|
|
return -1;
|
|
}
|
|
|
|
sockaddr_storage host_addr {};
|
|
SocketLength host_addrlen = sizeof(host_addr);
|
|
NativeSocket accepted =
|
|
::accept(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen);
|
|
if (accepted == INVALID_NATIVE_SOCKET) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
auto transport = std::make_shared<SocketTransport>(accepted);
|
|
const int fd = AllocSocketFd(transport);
|
|
if (fd < 0) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EMFILE;
|
|
return -1;
|
|
}
|
|
|
|
if (addr != nullptr || addrlen != nullptr) {
|
|
if (addr == nullptr || addrlen == nullptr) {
|
|
SocketClose(fd);
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EFAULT;
|
|
return -1;
|
|
}
|
|
if (ConvertHostSockaddr(&host_addr, host_addrlen, addr, addrlen) != 0) {
|
|
SocketClose(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Shutdown(int s, int how) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t how = %d\n",
|
|
s, how);
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
if (!GetSocketBackend(s, &socket)) {
|
|
return -1;
|
|
}
|
|
|
|
if (how < 0 || how > 2) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
if (::shutdown(socket, how) == SOCKET_ERROR) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
return 0;
|
|
#else
|
|
*Posix::GetErrorAddr() = Posix::POSIX_ENOSYS;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Getsockname(int s, void* addr, uint32_t* addrlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t addr = 0x%016" PRIx64 "\n"
|
|
"\t addrlen = 0x%016" PRIx64 "\n",
|
|
s, reinterpret_cast<uint64_t>(addr), reinterpret_cast<uint64_t>(addrlen));
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
SocketSlot state;
|
|
if (!GetSocketBackend(s, &socket, &state)) {
|
|
return -1;
|
|
}
|
|
if (addr == nullptr || addrlen == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EFAULT);
|
|
}
|
|
|
|
sockaddr_storage host_addr {};
|
|
SocketLength host_addrlen = sizeof(host_addr);
|
|
if (::getsockname(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen) != 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
const int result = ConvertHostSockaddr(&host_addr, host_addrlen, addr, addrlen);
|
|
if (result == 0 && state.p2p) {
|
|
std::memcpy(static_cast<uint8_t*>(addr) + 8, &state.p2p->virtual_port, sizeof(uint16_t));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Getsockopt(int s, int level, int optname, void* optval, uint32_t* optlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t level = 0x%08" PRIx32 "\n"
|
|
"\t optname = 0x%08" PRIx32 "\n",
|
|
s, static_cast<uint32_t>(level), static_cast<uint32_t>(optname));
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
SocketSlot state;
|
|
if (!GetSocketBackend(s, &socket, &state)) {
|
|
return -1;
|
|
}
|
|
if (optval == nullptr || optlen == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EFAULT);
|
|
}
|
|
if (state.p2p) {
|
|
const auto* value = level == 0xffff ? P2pSocketOption(*state.p2p, optname) : nullptr;
|
|
if (value == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_ENOPROTOOPT);
|
|
}
|
|
if (*optlen < sizeof(int)) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
std::memcpy(optval, value, sizeof(int));
|
|
*optlen = sizeof(int);
|
|
return 0;
|
|
}
|
|
|
|
// Guest socket options: SOL_SOCKET=0xffff, SO_ERROR=0x1007.
|
|
const bool socket_error = (level == 0xffff && optname == 0x1007);
|
|
#if !defined(_WIN32)
|
|
if (!socket_error) {
|
|
return SetGuestSocketError(Posix::POSIX_ENOPROTOOPT);
|
|
}
|
|
#endif
|
|
if (socket_error) {
|
|
optname = SO_ERROR;
|
|
}
|
|
SocketLength len = static_cast<SocketLength>(*optlen);
|
|
if (::getsockopt(socket, ConvertSocketOptionLevel(level), optname, static_cast<char*>(optval),
|
|
&len) != 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
if (socket_error && len >= static_cast<SocketLength>(sizeof(int))) {
|
|
auto* error = static_cast<int*>(optval);
|
|
*error = ConvertHostSocketError(*error);
|
|
}
|
|
*optlen = static_cast<uint32_t>(len);
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Setsockopt(int s, int level, int optname, const void* optval, uint32_t optlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t level = 0x%08" PRIx32 "\n"
|
|
"\t optname = 0x%08" PRIx32 "\n"
|
|
"\t optlen = %" PRIu32 "\n",
|
|
s, static_cast<uint32_t>(level), static_cast<uint32_t>(optname), optlen);
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
SocketSlot state;
|
|
if (optval == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EFAULT);
|
|
}
|
|
if (!GetSocketBackend(s, &socket, &state)) {
|
|
return -1;
|
|
}
|
|
if (state.p2p) {
|
|
Common::LockGuard lock(g_socket_mutex);
|
|
auto& slot = g_sockets[static_cast<size_t>(s)];
|
|
if (slot.transport != state.transport) {
|
|
return SetGuestSocketError(Posix::POSIX_EBADF);
|
|
}
|
|
auto* option = level == 0xffff ? P2pSocketOption(*slot.p2p, optname) : nullptr;
|
|
if (option == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_ENOPROTOOPT);
|
|
}
|
|
if (optlen != sizeof(int)) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
int value = 0;
|
|
std::memcpy(&value, optval, sizeof(value));
|
|
if (optname == 0x1001 || optname == 0x1002) {
|
|
if (value <= 0) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
*option = optname == 0x1002 ? std::min(value, 512 * 1024) : value;
|
|
} else {
|
|
*option = value != 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
constexpr int ORBIS_SO_NBIO = 0x1200;
|
|
if (ConvertSocketOptionLevel(level) == SOL_SOCKET && optname == ORBIS_SO_NBIO &&
|
|
optlen >= sizeof(int)) {
|
|
u_long enabled = (*static_cast<const int*>(optval) != 0 ? 1 : 0);
|
|
if (ioctlsocket(socket, FIONBIO, &enabled) == SOCKET_ERROR) {
|
|
return SetHostSocketError();
|
|
}
|
|
return 0;
|
|
}
|
|
#else
|
|
// Guest TCP options: IPPROTO_TCP=6, TCP_NODELAY=1.
|
|
if (level != 6 || optname != 1) {
|
|
return SetGuestSocketError(Posix::POSIX_ENOPROTOOPT);
|
|
}
|
|
level = IPPROTO_TCP;
|
|
optname = TCP_NODELAY;
|
|
#endif
|
|
|
|
if (::setsockopt(socket, ConvertSocketOptionLevel(level), optname,
|
|
static_cast<const char*>(optval), static_cast<SocketLength>(optlen)) != 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI Send(int s, const void* buf, uint64_t len, int flags) {
|
|
return Sendto(s, buf, len, flags, nullptr, 0);
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI Sendto(int s, const void* buf, uint64_t len, int flags, const void* addr,
|
|
uint32_t addrlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t buf = 0x%016" PRIx64 "\n"
|
|
"\t len = %" PRIu64 "\n"
|
|
"\t flags = 0x%08" PRIx32 "\n"
|
|
"\t addr = 0x%016" PRIx64 "\n"
|
|
"\t addrlen = %" PRIu32 "\n",
|
|
s, reinterpret_cast<uint64_t>(buf), len, static_cast<uint32_t>(flags),
|
|
reinterpret_cast<uint64_t>(addr), addrlen);
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
if (buf == nullptr) {
|
|
return SetGuestSocketError(Posix::POSIX_EFAULT);
|
|
}
|
|
// UDPP2P requires protocol framing; the native-only lookup rejects raw sends.
|
|
if (!GetSocketBackend(s, &socket)) {
|
|
return -1;
|
|
}
|
|
|
|
const int host_flags = ConvertMessageFlags(flags);
|
|
if (host_flags < 0) {
|
|
return -1;
|
|
}
|
|
|
|
const auto host_len = static_cast<SocketIoLength>(
|
|
std::min<uint64_t>(len, std::numeric_limits<SocketIoLength>::max()));
|
|
int64_t result = 0;
|
|
if (addr == nullptr) {
|
|
result = ::send(socket, static_cast<const char*>(buf), host_len, host_flags);
|
|
} else {
|
|
sockaddr_storage host_addr {};
|
|
SocketLength host_addrlen = 0;
|
|
if (ConvertGuestSockaddr(addr, addrlen, &host_addr, &host_addrlen) != 0) {
|
|
return -1;
|
|
}
|
|
result = ::sendto(socket, static_cast<const char*>(buf), host_len, host_flags,
|
|
reinterpret_cast<const sockaddr*>(&host_addr), host_addrlen);
|
|
}
|
|
if (result < 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI Recv(int s, void* buf, uint64_t len, int flags) {
|
|
return Recvfrom(s, buf, len, flags, nullptr, nullptr);
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void* addr,
|
|
uint32_t* addrlen) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t s = %d\n"
|
|
"\t buf = 0x%016" PRIx64 "\n"
|
|
"\t len = %" PRIu64 "\n"
|
|
"\t flags = 0x%08" PRIx32 "\n"
|
|
"\t addr = 0x%016" PRIx64 "\n"
|
|
"\t addrlen = 0x%016" PRIx64 "\n",
|
|
s, reinterpret_cast<uint64_t>(buf), len, static_cast<uint32_t>(flags),
|
|
reinterpret_cast<uint64_t>(addr), reinterpret_cast<uint64_t>(addrlen));
|
|
|
|
if (buf == nullptr || (addr != nullptr && addrlen == nullptr)) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EFAULT;
|
|
return -1;
|
|
}
|
|
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
if (!GetSocketBackend(s, &socket)) {
|
|
return -1;
|
|
}
|
|
|
|
const int host_flags = ConvertMessageFlags(flags);
|
|
if (host_flags < 0) {
|
|
return -1;
|
|
}
|
|
|
|
const auto host_len = static_cast<SocketIoLength>(
|
|
std::min<uint64_t>(len, std::numeric_limits<SocketIoLength>::max()));
|
|
sockaddr_storage host_addr {};
|
|
SocketLength host_addrlen = sizeof(host_addr);
|
|
int64_t result = 0;
|
|
if (addr == nullptr) {
|
|
result = ::recv(socket, static_cast<char*>(buf), host_len, host_flags);
|
|
} else {
|
|
result = ::recvfrom(socket, static_cast<char*>(buf), host_len, host_flags,
|
|
reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen);
|
|
}
|
|
#if defined(_WIN32)
|
|
if (result < 0 && WSAGetLastError() == WSAEMSGSIZE) {
|
|
// Winsock copied the datagram prefix; POSIX reports its length as success.
|
|
result = host_len;
|
|
}
|
|
#endif
|
|
if (result < 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
if (addr != nullptr && ConvertHostSockaddr(&host_addr, host_addrlen, addr, addrlen) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfds,
|
|
const void* timeout) {
|
|
PRINT_NAME();
|
|
|
|
if (nfds < 0 || nfds > SOCKET_FD_MAX) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
static std::atomic_uint32_t select_log_count = 0;
|
|
const bool log_select = select_log_count.fetch_add(1, std::memory_order_relaxed) < 64;
|
|
|
|
if (log_select) {
|
|
const auto read0 = (readfds != nullptr ? *static_cast<const uint64_t*>(readfds) : 0);
|
|
const auto write0 = (writefds != nullptr ? *static_cast<const uint64_t*>(writefds) : 0);
|
|
const auto except0 = (exceptfds != nullptr ? *static_cast<const uint64_t*>(exceptfds) : 0);
|
|
const auto* guest_timeout = static_cast<const NetTimeval*>(timeout);
|
|
LOGF("\t nfds = %d\n"
|
|
"\t read = 0x%016" PRIx64 " bits0=0x%016" PRIx64 "\n"
|
|
"\t write = 0x%016" PRIx64 " bits0=0x%016" PRIx64 "\n"
|
|
"\t except = 0x%016" PRIx64 " bits0=0x%016" PRIx64 "\n"
|
|
"\t timeout = 0x%016" PRIx64,
|
|
nfds, reinterpret_cast<uint64_t>(readfds), read0, reinterpret_cast<uint64_t>(writefds),
|
|
write0, reinterpret_cast<uint64_t>(exceptfds), except0,
|
|
reinterpret_cast<uint64_t>(timeout));
|
|
if (guest_timeout != nullptr) {
|
|
LOGF(" sec=%" PRId64 " usec=%" PRId64, guest_timeout->tv_sec, guest_timeout->tv_usec);
|
|
}
|
|
LOGF("\n");
|
|
}
|
|
|
|
fd_set host_read {};
|
|
fd_set host_write {};
|
|
fd_set host_except {};
|
|
FD_ZERO(&host_read);
|
|
FD_ZERO(&host_write);
|
|
FD_ZERO(&host_except);
|
|
|
|
std::vector<std::pair<int, NativeSocket>> descriptors;
|
|
int host_nfds = 0;
|
|
for (int fd = 0; fd < nfds; fd++) {
|
|
const bool read = GuestFdIsSet(readfds, fd);
|
|
const bool write = GuestFdIsSet(writefds, fd);
|
|
const bool except = GuestFdIsSet(exceptfds, fd);
|
|
if (!read && !write && !except) {
|
|
continue;
|
|
}
|
|
NativeSocket socket = INVALID_NATIVE_SOCKET;
|
|
#if !defined(_WIN32)
|
|
if (fd < 3) {
|
|
socket = fd;
|
|
} else
|
|
#endif
|
|
if (!GetSocketBackend(fd, &socket)) {
|
|
return -1;
|
|
}
|
|
#if defined(_WIN32)
|
|
if ((read && host_read.fd_count == FD_SETSIZE) ||
|
|
(write && host_write.fd_count == FD_SETSIZE) ||
|
|
(except && host_except.fd_count == FD_SETSIZE)) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
#else
|
|
if (socket >= FD_SETSIZE) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
host_nfds = std::max(host_nfds, socket + 1);
|
|
#endif
|
|
descriptors.emplace_back(fd, socket);
|
|
if (read) {
|
|
FD_SET(socket, &host_read);
|
|
}
|
|
if (write) {
|
|
FD_SET(socket, &host_write);
|
|
}
|
|
if (except) {
|
|
FD_SET(socket, &host_except);
|
|
}
|
|
}
|
|
|
|
timeval host_timeout {};
|
|
timeval* host_timeout_ptr = nullptr;
|
|
if (timeout != nullptr) {
|
|
const auto* guest_timeout = static_cast<const NetTimeval*>(timeout);
|
|
if (guest_timeout->tv_sec < 0 || guest_timeout->tv_usec < 0 ||
|
|
guest_timeout->tv_usec >= 1'000'000) {
|
|
return SetGuestSocketError(Posix::POSIX_EINVAL);
|
|
}
|
|
host_timeout.tv_sec = static_cast<decltype(host_timeout.tv_sec)>(guest_timeout->tv_sec);
|
|
host_timeout.tv_usec = static_cast<decltype(host_timeout.tv_usec)>(guest_timeout->tv_usec);
|
|
host_timeout_ptr = &host_timeout;
|
|
}
|
|
|
|
int result = 0;
|
|
#if defined(_WIN32)
|
|
if (descriptors.empty()) {
|
|
Sleep(host_timeout_ptr == nullptr ? INFINITE :
|
|
static_cast<DWORD>(host_timeout.tv_sec * 1000 + host_timeout.tv_usec / 1000));
|
|
} else
|
|
#endif
|
|
{
|
|
result = ::select(host_nfds, readfds != nullptr ? &host_read : nullptr,
|
|
writefds != nullptr ? &host_write : nullptr,
|
|
exceptfds != nullptr ? &host_except : nullptr, host_timeout_ptr);
|
|
if (result < 0) {
|
|
return SetHostSocketError();
|
|
}
|
|
}
|
|
|
|
GuestFdZero(readfds, nfds);
|
|
GuestFdZero(writefds, nfds);
|
|
GuestFdZero(exceptfds, nfds);
|
|
for (const auto& [fd, socket]: descriptors) {
|
|
if (FD_ISSET(socket, &host_read)) {
|
|
GuestFdSet(readfds, fd);
|
|
}
|
|
if (FD_ISSET(socket, &host_write)) {
|
|
GuestFdSet(writefds, fd);
|
|
}
|
|
if (FD_ISSET(socket, &host_except)) {
|
|
GuestFdSet(exceptfds, fd);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace Net
|
|
|
|
namespace Ssl {
|
|
|
|
LIB_NAME("Ssl", "Ssl");
|
|
|
|
struct SslData {
|
|
char* ptr = nullptr;
|
|
size_t size = 0;
|
|
};
|
|
|
|
struct SslCaCerts {
|
|
SslData* cert_data = nullptr;
|
|
size_t cert_data_num = 0;
|
|
void* pool = nullptr;
|
|
};
|
|
|
|
int KYTY_SYSV_ABI SslInit(uint64_t pool_size) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t size = %" PRIu64 "\n", pool_size);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(pool_size == 0);
|
|
|
|
auto id = g_net->SslInit(pool_size);
|
|
|
|
if (!id.IsValid()) {
|
|
return SSL_ERROR_OUT_OF_SIZE;
|
|
}
|
|
|
|
return id.ToInt();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI SslTerm(int ssl_ctx_id) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->SslTerm(Network::Id(ssl_ctx_id))) {
|
|
return SSL_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI SslGetCaCerts(int ssl_ctx_id, void* ca_certs) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t ssl_ctx_id = %d\n", ssl_ctx_id);
|
|
|
|
if (ca_certs == nullptr) {
|
|
return SSL_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->SslValid(Network::Id(ssl_ctx_id))) {
|
|
return SSL_ERROR_INVALID_ID;
|
|
}
|
|
|
|
auto* certs = static_cast<SslCaCerts*>(ca_certs);
|
|
certs->cert_data = nullptr;
|
|
certs->cert_data_num = 0;
|
|
certs->pool = nullptr;
|
|
|
|
return SSL_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI SslFreeCaCerts(int ssl_ctx_id, void* ca_certs) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t ssl_ctx_id = %d\n", ssl_ctx_id);
|
|
|
|
if (ca_certs == nullptr) {
|
|
return SSL_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->SslValid(Network::Id(ssl_ctx_id))) {
|
|
return SSL_ERROR_INVALID_ID;
|
|
}
|
|
|
|
auto* certs = static_cast<SslCaCerts*>(ca_certs);
|
|
certs->cert_data = nullptr;
|
|
certs->cert_data_num = 0;
|
|
certs->pool = nullptr;
|
|
|
|
return OK;
|
|
}
|
|
|
|
} // namespace Ssl
|
|
|
|
namespace Http {
|
|
|
|
struct HttpEpoll {
|
|
Network::Id http_ctx_id = Network::Id(0);
|
|
Network::Id request_id = Network::Id(0);
|
|
void* user_arg = nullptr;
|
|
};
|
|
|
|
struct HttpNBEvent {
|
|
uint32_t events = 0;
|
|
uint32_t event_detail = 0;
|
|
int id = 0;
|
|
void* user_arg = nullptr;
|
|
};
|
|
|
|
LIB_NAME("Http", "Http");
|
|
|
|
static const char* HttpMethodToString(int method) {
|
|
switch (method) {
|
|
case 0: return "GET";
|
|
case 1: return "POST";
|
|
case 2: return "HEAD";
|
|
case 3: return "OPTIONS";
|
|
case 4: return "PUT";
|
|
case 5: return "DELETE";
|
|
case 6: return "TRACE";
|
|
case 7: return "CONNECT";
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpInit(int memid, int ssl_ctx_id, uint64_t pool_size) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t memid = %d\n"
|
|
"\t ssl_ctx_id = %d\n"
|
|
"\t size = %" PRIu64 "\n",
|
|
memid, ssl_ctx_id, pool_size);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(pool_size == 0);
|
|
|
|
auto id = g_net->HttpInit(memid, Network::Id(ssl_ctx_id), pool_size);
|
|
|
|
if (!id.IsValid()) {
|
|
return HTTP_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return id.ToInt();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpTerm(int http_ctx_id) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->HttpTerm(Network::Id(http_ctx_id))) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpCreateTemplate(int http_ctx_id, const char* user_agent, int http_ver,
|
|
int is_auto_proxy_conf) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t http_ctx_id = %d\n"
|
|
"\t user_agent = %s\n"
|
|
"\t http_ver = %d\n"
|
|
"\t is_auto_proxy_conf = %d\n",
|
|
http_ctx_id, user_agent, http_ver, is_auto_proxy_conf);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
auto id = g_net->HttpCreateTemplate(Network::Id(http_ctx_id), user_agent, http_ver,
|
|
is_auto_proxy_conf != 0);
|
|
|
|
if (!id.IsValid()) {
|
|
return HTTP_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return id.ToInt();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpDeleteTemplate(int tmpl_id) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->HttpDeleteTemplate(Network::Id(tmpl_id))) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetNonblock(int id, int enable) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t enable = %d\n",
|
|
id, enable);
|
|
|
|
if (!g_net->HttpSetNonblock(Network::Id(id), enable != 0)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpsSetSslCallback(int id, HttpsCallback cbfunc, void* user_arg) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n", id);
|
|
|
|
if (!g_net->HttpsSetSslCallback(Network::Id(id), cbfunc, user_arg)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpsSetMinSslVersion(int id, uint32_t ssl_version) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t ssl_version = %u\n",
|
|
id, ssl_version);
|
|
|
|
if (!g_net->HttpsSetMinSslVersion(Network::Id(id), ssl_version)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpsDisableOption(int id, uint32_t ssl_flags) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t ssl_flags = %u\n",
|
|
id, ssl_flags);
|
|
|
|
if (!g_net->HttpsDisableOption(Network::Id(id), ssl_flags)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetResolveTimeOut(int id, uint32_t usec) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t usec = %u\n",
|
|
id, usec);
|
|
|
|
if (!g_net->HttpSetResolveTimeOut(Network::Id(id), usec)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetResolveRetry(int id, int32_t retry) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t retry = %d\n",
|
|
id, retry);
|
|
|
|
if (!g_net->HttpSetResolveRetry(Network::Id(id), retry)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetConnectTimeOut(int id, uint32_t usec) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t usec = %u\n",
|
|
id, usec);
|
|
|
|
if (!g_net->HttpSetConnectTimeOut(Network::Id(id), usec)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetSendTimeOut(int id, uint32_t usec) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t usec = %u\n",
|
|
id, usec);
|
|
|
|
if (!g_net->HttpSetSendTimeOut(Network::Id(id), usec)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetRecvTimeOut(int id, uint32_t usec) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t usec = %u\n",
|
|
id, usec);
|
|
|
|
if (!g_net->HttpSetRecvTimeOut(Network::Id(id), usec)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetAutoRedirect(int id, int enable) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t enable = %d\n",
|
|
id, enable);
|
|
|
|
if (!g_net->HttpSetAutoRedirect(Network::Id(id), enable)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetAuthEnabled(int id, int enable) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t enable = %d\n",
|
|
id, enable);
|
|
|
|
if (!g_net->HttpSetAuthEnabled(Network::Id(id), enable)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n"
|
|
"\t name = %s\n"
|
|
"\t value = %s\n"
|
|
"\t mode = %u\n",
|
|
id, name, value, mode);
|
|
|
|
EXIT_NOT_IMPLEMENTED(mode != 0 && mode != 1);
|
|
|
|
if (!g_net->HttpAddRequestHeader(Network::Id(id), name, value, mode == 1)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpCreateEpoll(int http_ctx_id, HttpEpollHandle* eh) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t http_ctx_id = %d\n", http_ctx_id);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(eh == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(!g_net->HttpValid(Network::Id(http_ctx_id)));
|
|
|
|
*eh = new HttpEpoll;
|
|
|
|
(*eh)->http_ctx_id = Network::Id(http_ctx_id);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpDestroyEpoll(int http_ctx_id, HttpEpollHandle eh) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t http_ctx_id = %d\n", http_ctx_id);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(eh == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(!g_net->HttpValid(Network::Id(http_ctx_id)));
|
|
|
|
delete eh;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetEpoll(int id, HttpEpollHandle eh, void* user_arg) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n", id);
|
|
|
|
EXIT_NOT_IMPLEMENTED(eh == nullptr);
|
|
|
|
EXIT_NOT_IMPLEMENTED(!g_net->HttpValidRequest(Network::Id(id)));
|
|
|
|
eh->request_id = Network::Id(id);
|
|
eh->user_arg = user_arg;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpUnsetEpoll(int id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t id = %d\n", id);
|
|
|
|
EXIT_NOT_IMPLEMENTED(!g_net->HttpValidRequest(Network::Id(id)));
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSendRequest(int request_id, const void* /*post_data*/, size_t /*size*/) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t request_id = %d\n", request_id);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->HttpMarkRequestSent(Network::Id(request_id), HTTP_ERROR_TIMEOUT)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return HTTP_ERROR_TIMEOUT;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpAbortRequest(int request_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t request_id = %d\n", request_id);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->HttpValidRequest(Network::Id(request_id))) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpWaitRequest(HttpEpollHandle eh, HttpNBEvent* nbev, int maxevents,
|
|
int timeout) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t eh = 0x%016" PRIx64 "\n"
|
|
"\t nbev = 0x%016" PRIx64 "\n"
|
|
"\t maxevents = %d\n"
|
|
"\t timeout = %d\n",
|
|
reinterpret_cast<uint64_t>(eh), reinterpret_cast<uint64_t>(nbev), maxevents, timeout);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (eh == nullptr || maxevents < 0 || (maxevents > 0 && nbev == nullptr)) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpGetStatusCode(int request_id, int* status_code) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t request_id = %d\n"
|
|
"\t status_code = 0x%016" PRIx64 "\n",
|
|
request_id, reinterpret_cast<uint64_t>(status_code));
|
|
|
|
if (status_code == nullptr) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
|
|
*status_code = 0;
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
int send_result = HTTP_ERROR_BEFORE_SEND;
|
|
if (!g_net->HttpGetRequestResponse(Network::Id(request_id), &send_result, status_code, nullptr,
|
|
nullptr, nullptr)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return send_result;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpGetAllResponseHeaders(int request_id, char** header, size_t* header_size) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t request_id = %d\n"
|
|
"\t header = 0x%016" PRIx64 "\n"
|
|
"\t header_size = 0x%016" PRIx64 "\n",
|
|
request_id, reinterpret_cast<uint64_t>(header), reinterpret_cast<uint64_t>(header_size));
|
|
|
|
if (header == nullptr || header_size == nullptr) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
|
|
*header = nullptr;
|
|
*header_size = 0;
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
int send_result = HTTP_ERROR_BEFORE_SEND;
|
|
const char* headers = nullptr;
|
|
size_t headers_size = 0;
|
|
if (!g_net->HttpGetRequestResponse(Network::Id(request_id), &send_result, nullptr, &headers,
|
|
&headers_size, nullptr)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
*header = const_cast<char*>(headers);
|
|
*header_size = headers_size;
|
|
|
|
return send_result;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpGetResponseContentLength(int request_id, int* result,
|
|
uint64_t* content_length) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t request_id = %d\n"
|
|
"\t result = 0x%016" PRIx64 "\n"
|
|
"\t content_length = 0x%016" PRIx64 "\n",
|
|
request_id, reinterpret_cast<uint64_t>(result),
|
|
reinterpret_cast<uint64_t>(content_length));
|
|
|
|
if (result == nullptr || content_length == nullptr) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
|
|
*result = 0;
|
|
*content_length = 0;
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
int send_result = HTTP_ERROR_BEFORE_SEND;
|
|
if (!g_net->HttpGetRequestResponse(Network::Id(request_id), &send_result, nullptr, nullptr,
|
|
nullptr, content_length)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
*result = (send_result == OK ? 0 : -1);
|
|
return send_result;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpCreateConnection(int tmpl_id, const char* server_name, const char* scheme,
|
|
uint16_t port, int enable_keep_alive) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t tmpl_id = %d\n"
|
|
"\t server_name = %s\n"
|
|
"\t scheme = %s\n"
|
|
"\t port = %u\n"
|
|
"\t enable_keep_alive = %d\n",
|
|
tmpl_id, server_name, scheme, static_cast<unsigned>(port), enable_keep_alive);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (server_name == nullptr || scheme == nullptr) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
|
|
auto id = g_net->HttpCreateConnection(Network::Id(tmpl_id), server_name, scheme, port,
|
|
enable_keep_alive != 0);
|
|
|
|
if (!id.IsValid()) {
|
|
return HTTP_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return id.ToInt();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpCreateConnectionWithURL(int tmpl_id, const char* url, int enable_keep_alive) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t tmpl_id = %d\n"
|
|
"\t url = %s\n"
|
|
"\t enable_keep_alive = %d\n",
|
|
tmpl_id, url, enable_keep_alive);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (url == nullptr || url[0] == '\0') {
|
|
return HTTP_ERROR_INVALID_URL;
|
|
}
|
|
|
|
auto id = g_net->HttpCreateConnectionWithURL(Network::Id(tmpl_id), url, enable_keep_alive != 0);
|
|
|
|
if (!id.IsValid()) {
|
|
return HTTP_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return id.ToInt();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpDeleteConnection(int conn_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t conn_id = %d\n", conn_id);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->HttpDeleteConnection(Network::Id(conn_id))) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpCreateRequest(int conn_id, int method, const char* path,
|
|
uint64_t content_length) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t conn_id = %d\n"
|
|
"\t method = %d\n"
|
|
"\t path = %s\n"
|
|
"\t content_length = %" PRIu64 "\n",
|
|
conn_id, method, path, content_length);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
auto method_name = HttpMethodToString(method);
|
|
if (method_name == nullptr) {
|
|
return HTTP_ERROR_UNKNOWN_METHOD;
|
|
}
|
|
if (path == nullptr) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
|
|
auto id =
|
|
g_net->HttpCreateRequestWithURL2(Network::Id(conn_id), method_name, path, content_length);
|
|
|
|
if (!id.IsValid()) {
|
|
return HTTP_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return id.ToInt();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpCreateRequest2(int conn_id, const char* method, const char* path,
|
|
uint64_t content_length) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t conn_id = %d\n"
|
|
"\t method = %s\n"
|
|
"\t path = %s\n"
|
|
"\t content_length = %" PRIu64 "\n",
|
|
conn_id, method, path, content_length);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (method == nullptr || path == nullptr) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
|
|
auto id =
|
|
g_net->HttpCreateRequestWithURL2(Network::Id(conn_id), method, path, content_length);
|
|
return id.IsValid() ? id.ToInt() : HTTP_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpCreateRequestWithURL2(int conn_id, const char* method, const char* url,
|
|
uint64_t content_length) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t conn_id = %d\n"
|
|
"\t url = %s\n"
|
|
"\t method = %s\n"
|
|
"\t content_length = %" PRIu64 "\n",
|
|
conn_id, url, method, content_length);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (method == nullptr) {
|
|
return HTTP_ERROR_INVALID_VALUE;
|
|
}
|
|
if (url == nullptr || url[0] == '\0') {
|
|
return HTTP_ERROR_INVALID_URL;
|
|
}
|
|
|
|
auto id = g_net->HttpCreateRequestWithURL2(Network::Id(conn_id), method, url, content_length);
|
|
|
|
if (!id.IsValid()) {
|
|
return HTTP_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return id.ToInt();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpDeleteRequest(int req_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->HttpDeleteRequest(Network::Id(req_id))) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI HttpSetRequestContentLength(int request_id, uint64_t content_length) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t request_id = %d\n"
|
|
"\t content_length = %" PRIu64 "\n",
|
|
request_id, content_length);
|
|
|
|
EXIT_IF(g_net == nullptr);
|
|
|
|
if (!g_net->HttpSetRequestContentLength(Network::Id(request_id), content_length)) {
|
|
return HTTP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
} // namespace Http
|
|
|
|
namespace NetCtl {
|
|
|
|
LIB_NAME("NetCtl", "NetCtl");
|
|
|
|
constexpr int NET_CTL_STATE_DISCONNECTED = 0;
|
|
constexpr int NET_CTL_STATE_IPOBTAINED = 3;
|
|
constexpr int NET_CTL_DEVICE_WIRED = 0;
|
|
constexpr int NET_CTL_DEVICE_WIRELESS = 1;
|
|
constexpr int NET_CTL_LINK_DISCONNECTED = 0;
|
|
constexpr int NET_CTL_LINK_CONNECTED = 1;
|
|
constexpr int NET_CTL_EVENT_DISCONNECTED = 1;
|
|
constexpr int NET_CTL_EVENT_IPOBTAINED = 3;
|
|
constexpr int NET_CTL_IP_DHCP = 0;
|
|
constexpr int NET_CTL_HTTP_PROXY_OFF = 0;
|
|
constexpr int NET_CTL_CALLBACK_MAX = 8;
|
|
constexpr int NET_CTL_ERROR_CALLBACK_MAX = -2143215357; /* 0x80412103 */
|
|
constexpr int NET_CTL_ERROR_ID_NOT_FOUND = -2143215356; /* 0x80412104 */
|
|
constexpr int NET_CTL_ERROR_INVALID_ID = -2143215355; /* 0x80412105 */
|
|
constexpr int NET_CTL_ERROR_INVALID_CODE = -2143215354; /* 0x80412106 */
|
|
constexpr int NET_CTL_ERROR_INVALID_ADDR = -2143215353; /* 0x80412107 */
|
|
constexpr int NET_CTL_ERROR_NOT_CONNECTED = -2143215352; /* 0x80412108 */
|
|
constexpr int NET_CTL_ERROR_INVALID_TYPE = -2143215345; /* 0x8041210f */
|
|
|
|
struct NetInAddr {
|
|
uint32_t s_addr = 0;
|
|
};
|
|
|
|
struct NetEtherAddr {
|
|
uint8_t data[6];
|
|
};
|
|
|
|
struct NetCtlNatInfo {
|
|
unsigned int size = sizeof(NetCtlNatInfo);
|
|
int stunStatus = 0;
|
|
int natType = 0;
|
|
NetInAddr mappedAddr;
|
|
};
|
|
|
|
union NetCtlInfo {
|
|
uint32_t device;
|
|
NetEtherAddr ether_addr;
|
|
uint32_t mtu;
|
|
uint32_t link;
|
|
NetEtherAddr bssid;
|
|
char ssid[32 + 1];
|
|
uint32_t wifi_security;
|
|
int32_t rssi_dbm;
|
|
uint8_t rssi_percentage;
|
|
uint8_t channel;
|
|
uint32_t ip_config;
|
|
char dhcp_hostname[255 + 1];
|
|
char pppoe_auth_name[127 + 1];
|
|
char ip_address[16];
|
|
char netmask[16];
|
|
char default_route[16];
|
|
char primary_dns[16];
|
|
char secondary_dns[16];
|
|
uint32_t http_proxy_config;
|
|
char http_proxy_server[255 + 1];
|
|
uint16_t http_proxy_port;
|
|
};
|
|
|
|
struct NetCtlCallbackSlot {
|
|
NetCtlCallback func = nullptr;
|
|
void* arg = nullptr;
|
|
int last_event = 0;
|
|
};
|
|
|
|
static Common::Mutex g_net_ctl_callbacks_mutex;
|
|
static std::array<NetCtlCallbackSlot, 8> g_net_ctl_callbacks;
|
|
static std::atomic_bool g_net_ctl_connected = false;
|
|
static std::atomic_bool g_net_ctl_status_initialized = false;
|
|
|
|
struct HostNetworkInfo {
|
|
bool connected = false;
|
|
bool wireless = false;
|
|
NetEtherAddr ether_addr {};
|
|
char ip_address[16] {};
|
|
char netmask[16] {};
|
|
char default_route[16] {};
|
|
char primary_dns[16] {};
|
|
char secondary_dns[16] {};
|
|
};
|
|
|
|
static void CopyIpv4String(char* dst, size_t dst_size, const sockaddr* addr) {
|
|
#if defined(_WIN32)
|
|
if (dst == nullptr || dst_size == 0 || addr == nullptr || addr->sa_family != AF_INET) {
|
|
return;
|
|
}
|
|
|
|
const auto* in = reinterpret_cast<const sockaddr_in*>(addr);
|
|
inet_ntop(AF_INET, &in->sin_addr, dst, static_cast<socklen_t>(dst_size));
|
|
#else
|
|
(void)dst;
|
|
(void)dst_size;
|
|
(void)addr;
|
|
#endif
|
|
}
|
|
|
|
static void CopyIpv4Netmask(char* dst, size_t dst_size, uint8_t prefix_len) {
|
|
#if defined(_WIN32)
|
|
if (dst == nullptr || dst_size == 0 || prefix_len > 32) {
|
|
return;
|
|
}
|
|
|
|
const uint32_t mask = (prefix_len == 0 ? 0 : (0xffffffffu << (32u - prefix_len)));
|
|
in_addr in {};
|
|
in.S_un.S_addr = htonl(mask);
|
|
inet_ntop(AF_INET, &in, dst, static_cast<socklen_t>(dst_size));
|
|
#else
|
|
(void)dst;
|
|
(void)dst_size;
|
|
(void)prefix_len;
|
|
#endif
|
|
}
|
|
|
|
static HostNetworkInfo QueryHostNetworkInfo() {
|
|
HostNetworkInfo info;
|
|
#if defined(_WIN32)
|
|
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST;
|
|
ULONG size = 0;
|
|
auto ret = GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, nullptr, &size);
|
|
if (ret != ERROR_BUFFER_OVERFLOW || size == 0) {
|
|
return info;
|
|
}
|
|
|
|
std::vector<uint8_t> buffer(size);
|
|
auto* adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buffer.data());
|
|
ret = GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, adapters, &size);
|
|
if (ret != NO_ERROR) {
|
|
return info;
|
|
}
|
|
|
|
for (auto* adapter = adapters; adapter != nullptr; adapter = adapter->Next) {
|
|
if (adapter->OperStatus != IfOperStatusUp || adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK ||
|
|
adapter->IfType == IF_TYPE_TUNNEL) {
|
|
continue;
|
|
}
|
|
|
|
for (auto* unicast = adapter->FirstUnicastAddress; unicast != nullptr;
|
|
unicast = unicast->Next) {
|
|
const auto* addr = unicast->Address.lpSockaddr;
|
|
if (addr == nullptr) {
|
|
continue;
|
|
}
|
|
if (addr->sa_family == AF_INET) {
|
|
const auto* in = reinterpret_cast<const sockaddr_in*>(addr);
|
|
const auto ip = ntohl(in->sin_addr.S_un.S_addr);
|
|
if ((ip & 0xff000000u) != 0x7f000000u && ip != 0) {
|
|
info.connected = true;
|
|
info.wireless = (adapter->IfType == IF_TYPE_IEEE80211);
|
|
if (adapter->PhysicalAddressLength >= sizeof(info.ether_addr.data)) {
|
|
std::memcpy(info.ether_addr.data, adapter->PhysicalAddress,
|
|
sizeof(info.ether_addr.data));
|
|
}
|
|
CopyIpv4String(info.ip_address, sizeof(info.ip_address), addr);
|
|
CopyIpv4Netmask(info.netmask, sizeof(info.netmask),
|
|
unicast->OnLinkPrefixLength);
|
|
if (adapter->FirstGatewayAddress != nullptr) {
|
|
CopyIpv4String(info.default_route, sizeof(info.default_route),
|
|
adapter->FirstGatewayAddress->Address.lpSockaddr);
|
|
}
|
|
int dns_index = 0;
|
|
for (auto* dns = adapter->FirstDnsServerAddress; dns != nullptr;
|
|
dns = dns->Next) {
|
|
if (dns->Address.lpSockaddr != nullptr &&
|
|
dns->Address.lpSockaddr->sa_family == AF_INET) {
|
|
CopyIpv4String(dns_index == 0 ? info.primary_dns : info.secondary_dns,
|
|
dns_index == 0 ? sizeof(info.primary_dns)
|
|
: sizeof(info.secondary_dns),
|
|
dns->Address.lpSockaddr);
|
|
dns_index++;
|
|
if (dns_index >= 2) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return info;
|
|
}
|
|
} else if (addr->sa_family == AF_INET6) {
|
|
const auto* in6 = reinterpret_cast<const sockaddr_in6*>(addr);
|
|
if (!IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr) &&
|
|
!IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
|
|
info.connected = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return info;
|
|
#else
|
|
info.connected = true;
|
|
return info;
|
|
#endif
|
|
}
|
|
|
|
static bool NetCtlConnected() {
|
|
if (!g_net_ctl_status_initialized.load()) {
|
|
g_net_ctl_connected = false;
|
|
g_net_ctl_status_initialized = true;
|
|
LOGF("\t host network connected = false (forced offline)\n");
|
|
}
|
|
|
|
return g_net_ctl_connected.load();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlInit() {
|
|
PRINT_NAME();
|
|
|
|
g_net_ctl_connected = false;
|
|
g_net_ctl_status_initialized = true;
|
|
LOGF("\t host network connected = false (forced offline)\n");
|
|
|
|
return OK;
|
|
}
|
|
|
|
void KYTY_SYSV_ABI NetCtlTerm() {
|
|
PRINT_NAME();
|
|
|
|
Common::LockGuard lock(g_net_ctl_callbacks_mutex);
|
|
|
|
for (auto& cb: g_net_ctl_callbacks) {
|
|
cb = {};
|
|
}
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlGetNatInfo(NetCtlNatInfo* nat_info) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(nat_info == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(nat_info->size != sizeof(NetCtlNatInfo));
|
|
|
|
nat_info->stunStatus = 0;
|
|
nat_info->natType = 0;
|
|
nat_info->mappedAddr.s_addr = 0;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlCheckCallback() {
|
|
PRINT_NAME();
|
|
|
|
Common::LockGuard lock(g_net_ctl_callbacks_mutex);
|
|
const int event = (NetCtlConnected() ? NET_CTL_EVENT_IPOBTAINED : NET_CTL_EVENT_DISCONNECTED);
|
|
for (auto& cb: g_net_ctl_callbacks) {
|
|
if (cb.func != nullptr && cb.last_event != event) {
|
|
cb.func(event, cb.arg);
|
|
cb.last_event = event;
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlGetState(int* state) {
|
|
PRINT_NAME();
|
|
|
|
if (state == nullptr) {
|
|
return NET_CTL_ERROR_INVALID_ADDR;
|
|
}
|
|
|
|
*state = (NetCtlConnected() ? NET_CTL_STATE_IPOBTAINED : NET_CTL_STATE_DISCONNECTED);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlGetStateV6(int* state) {
|
|
PRINT_NAME();
|
|
|
|
if (state == nullptr) {
|
|
return NET_CTL_ERROR_INVALID_ADDR;
|
|
}
|
|
|
|
*state = (NetCtlConnected() ? NET_CTL_STATE_IPOBTAINED : NET_CTL_STATE_DISCONNECTED);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlRegisterCallback(NetCtlCallback func, void* arg, int* cid) {
|
|
PRINT_NAME();
|
|
|
|
if (func == nullptr || cid == nullptr) {
|
|
return NET_CTL_ERROR_INVALID_ADDR;
|
|
}
|
|
|
|
Common::LockGuard lock(g_net_ctl_callbacks_mutex);
|
|
|
|
for (int i = 0; i < NET_CTL_CALLBACK_MAX; i++) {
|
|
if (g_net_ctl_callbacks[static_cast<size_t>(i)].func == nullptr) {
|
|
g_net_ctl_callbacks[static_cast<size_t>(i)].func = func;
|
|
g_net_ctl_callbacks[static_cast<size_t>(i)].arg = arg;
|
|
g_net_ctl_callbacks[static_cast<size_t>(i)].last_event = 0;
|
|
*cid = i;
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
return NET_CTL_ERROR_CALLBACK_MAX;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlUnregisterCallback(int cid) {
|
|
PRINT_NAME();
|
|
|
|
if (cid < 0 || cid >= NET_CTL_CALLBACK_MAX) {
|
|
return NET_CTL_ERROR_INVALID_ID;
|
|
}
|
|
|
|
Common::LockGuard lock(g_net_ctl_callbacks_mutex);
|
|
|
|
auto& cb = g_net_ctl_callbacks[static_cast<size_t>(cid)];
|
|
|
|
if (cb.func == nullptr) {
|
|
return NET_CTL_ERROR_ID_NOT_FOUND;
|
|
}
|
|
|
|
cb = {};
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlGetResult(int event_type, int* error_code) {
|
|
PRINT_NAME();
|
|
|
|
if (error_code == nullptr) {
|
|
return NET_CTL_ERROR_INVALID_ADDR;
|
|
}
|
|
|
|
LOGF("\t event_type = %d\n", event_type);
|
|
|
|
switch (event_type) {
|
|
case NET_CTL_EVENT_DISCONNECTED:
|
|
case 2:
|
|
case NET_CTL_EVENT_IPOBTAINED: *error_code = OK; break;
|
|
default: return NET_CTL_ERROR_INVALID_TYPE;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NetCtlGetInfo(int code, NetCtlInfo* info) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(info == nullptr);
|
|
|
|
memset(info, 0, sizeof(NetCtlInfo));
|
|
|
|
// Online/host-backed info responses are left below for testing, but disabled
|
|
// to preserve the disconnected-console behavior.
|
|
return NET_CTL_ERROR_NOT_CONNECTED;
|
|
|
|
switch (code) {
|
|
case 1:
|
|
info->device =
|
|
(QueryHostNetworkInfo().wireless ? NET_CTL_DEVICE_WIRELESS : NET_CTL_DEVICE_WIRED);
|
|
break;
|
|
case 2: info->ether_addr = QueryHostNetworkInfo().ether_addr; break;
|
|
case 3: info->mtu = 1500; break;
|
|
case 4:
|
|
info->link = (NetCtlConnected() ? NET_CTL_LINK_CONNECTED : NET_CTL_LINK_DISCONNECTED);
|
|
break;
|
|
case 5: break;
|
|
case 6: break;
|
|
case 7: info->wifi_security = 0; break;
|
|
case 8: info->rssi_dbm = 0; break;
|
|
case 9: info->rssi_percentage = 0; break;
|
|
case 10: info->channel = 0; break;
|
|
case 11: info->ip_config = NET_CTL_IP_DHCP; break;
|
|
case 12: break;
|
|
case 13: break;
|
|
case 14:
|
|
std::strncpy(info->ip_address, QueryHostNetworkInfo().ip_address,
|
|
sizeof(info->ip_address) - 1);
|
|
break;
|
|
case 15:
|
|
std::strncpy(info->netmask, QueryHostNetworkInfo().netmask, sizeof(info->netmask) - 1);
|
|
break;
|
|
case 16:
|
|
std::strncpy(info->default_route, QueryHostNetworkInfo().default_route,
|
|
sizeof(info->default_route) - 1);
|
|
break;
|
|
case 17:
|
|
std::strncpy(info->primary_dns, QueryHostNetworkInfo().primary_dns,
|
|
sizeof(info->primary_dns) - 1);
|
|
break;
|
|
case 18:
|
|
std::strncpy(info->secondary_dns, QueryHostNetworkInfo().secondary_dns,
|
|
sizeof(info->secondary_dns) - 1);
|
|
break;
|
|
case 19: info->http_proxy_config = NET_CTL_HTTP_PROXY_OFF; break;
|
|
case 20: break;
|
|
case 21: info->http_proxy_port = 0; break;
|
|
default: LOGF("\t unknown NetCtl info code: %d\n", code); return NET_CTL_ERROR_INVALID_CODE;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
} // namespace NetCtl
|
|
|
|
namespace NpManager {
|
|
|
|
LIB_NAME("NpManager", "NpManager");
|
|
|
|
struct NpTitleId {
|
|
char id[12 + 1];
|
|
uint8_t padding[3];
|
|
};
|
|
|
|
struct NpTitleSecret {
|
|
uint8_t data[128];
|
|
};
|
|
|
|
struct NpCountryCode {
|
|
char data[2];
|
|
char term;
|
|
char padding[1];
|
|
};
|
|
|
|
struct NpAgeRestriction {
|
|
NpCountryCode country_code;
|
|
int8_t age;
|
|
uint8_t padding[3];
|
|
};
|
|
|
|
struct NpContentRestriction {
|
|
size_t size;
|
|
int8_t default_age_restriction;
|
|
char padding[3];
|
|
int32_t age_restriction_count;
|
|
const NpAgeRestriction* age_restriction;
|
|
};
|
|
|
|
struct NpOnlineId {
|
|
char data[16];
|
|
char term;
|
|
char dummy[3];
|
|
};
|
|
|
|
struct NpId {
|
|
NpOnlineId handle;
|
|
uint8_t opt[8];
|
|
uint8_t reserved[8];
|
|
};
|
|
|
|
struct NpCreateAsyncRequestParameter {
|
|
size_t size;
|
|
LibKernel::KernelCpumask cpu_affinity_mask;
|
|
int thread_priority;
|
|
uint8_t padding[4];
|
|
};
|
|
|
|
struct NpCheckPremiumParameter {
|
|
size_t size;
|
|
int user_id;
|
|
char padding[4];
|
|
uint64_t features;
|
|
uint8_t reserved[32];
|
|
};
|
|
|
|
struct NpCheckPremiumResult {
|
|
bool authorized;
|
|
uint8_t reserved[32];
|
|
};
|
|
|
|
constexpr int np_error_invalid_argument = -2141913085; /* 0x80550003 */
|
|
constexpr int np_error_signed_out = -2141913082; /* 0x80550006 */
|
|
constexpr int np_error_callback_already_registered = -2141913080; /* 0x80550008 */
|
|
constexpr int np_error_invalid_size = -2141913071; /* 0x80550011 */
|
|
constexpr int np_error_aborted = -2141913070; /* 0x80550012 */
|
|
constexpr int np_error_request_max = -2141913069; /* 0x80550013 */
|
|
constexpr int np_error_request_not_found = -2141913068; /* 0x80550014 */
|
|
constexpr int np_error_invalid_id = -2141913067; /* 0x80550015 */
|
|
constexpr int np_request_max = 128;
|
|
constexpr uint32_t np_state_signed_out = 1;
|
|
|
|
struct NpStateCallback {
|
|
NpStateCallbackA callback = nullptr;
|
|
void* userdata = nullptr;
|
|
bool pending = false;
|
|
};
|
|
|
|
static std::vector<NpStateCallback> g_np_state_callbacks;
|
|
|
|
enum class NpRequestState {
|
|
Free,
|
|
Ready,
|
|
Aborted,
|
|
Complete,
|
|
};
|
|
|
|
struct NpRequest {
|
|
NpRequestState state = NpRequestState::Free;
|
|
bool async = false;
|
|
int result = OK;
|
|
};
|
|
|
|
static std::mutex g_np_request_mutex;
|
|
static std::vector<NpRequest> g_np_requests;
|
|
|
|
static int np_create_request(bool async) {
|
|
std::lock_guard lock(g_np_request_mutex);
|
|
|
|
for (size_t i = 0; i < g_np_requests.size(); i++) {
|
|
auto& request = g_np_requests[i];
|
|
if (request.state == NpRequestState::Free) {
|
|
request.state = NpRequestState::Ready;
|
|
request.async = async;
|
|
request.result = OK;
|
|
return static_cast<int>(i + 1);
|
|
}
|
|
}
|
|
|
|
if (g_np_requests.size() >= np_request_max) {
|
|
return np_error_request_max;
|
|
}
|
|
|
|
g_np_requests.push_back({NpRequestState::Ready, async, OK});
|
|
return static_cast<int>(g_np_requests.size());
|
|
}
|
|
|
|
static NpRequest* np_get_request_locked(int req_id) {
|
|
if (req_id <= 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
const auto index = static_cast<size_t>(req_id - 1);
|
|
if (index >= g_np_requests.size() || g_np_requests[index].state == NpRequestState::Free) {
|
|
return nullptr;
|
|
}
|
|
|
|
return &g_np_requests[index];
|
|
}
|
|
|
|
static int np_complete_signed_out_locked(NpRequest* request) {
|
|
if (request->state == NpRequestState::Complete) {
|
|
request->result = np_error_invalid_argument;
|
|
return np_error_invalid_argument;
|
|
}
|
|
if (request->state == NpRequestState::Aborted) {
|
|
request->result = np_error_aborted;
|
|
return np_error_aborted;
|
|
}
|
|
|
|
request->state = NpRequestState::Complete;
|
|
request->result = np_error_signed_out;
|
|
|
|
return (request->async ? OK : np_error_signed_out);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpCheckCallback() {
|
|
PRINT_NAME();
|
|
|
|
std::vector<NpStateCallback> pending;
|
|
for (auto& entry: g_np_state_callbacks) {
|
|
if (entry.pending) {
|
|
pending.push_back(entry);
|
|
entry.pending = false;
|
|
}
|
|
}
|
|
for (const auto& entry: pending) {
|
|
entry.callback(Config::GetUserId(), np_state_signed_out, entry.userdata);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpSetNpTitleId(const NpTitleId* title_id, const NpTitleSecret* title_secret) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(title_id == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(title_secret == nullptr);
|
|
|
|
LOGF("\t title_id = %.12s\n"
|
|
"\t title_secret = %s\n",
|
|
title_id->id, fmt::format("{:02X}", fmt::join(title_secret->data, "")));
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpSetContentRestriction(const NpContentRestriction* restriction) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(restriction == nullptr);
|
|
EXIT_NOT_IMPLEMENTED(restriction->size != sizeof(NpContentRestriction));
|
|
|
|
LOGF("\t default_age_restriction = %" PRIi8 "\n"
|
|
"\t age_restriction_count = %" PRIi32 "\n",
|
|
restriction->default_age_restriction, restriction->age_restriction_count);
|
|
|
|
for (int i = 0; i < restriction->age_restriction_count; i++) {
|
|
LOGF("\t age_restriction[%d].age = %" PRIi8 "\n"
|
|
"\t age_restriction[%d].country_code.data = %.2s\n",
|
|
i, restriction->age_restriction[i].age, i,
|
|
restriction->age_restriction[i].country_code.data);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpRegisterStateCallback(void* /*callback*/, void* /*userdata*/) {
|
|
PRINT_NAME();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpUnregisterStateCallback() {
|
|
PRINT_NAME();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpRegisterStateCallbackA(NpStateCallbackA callback, void* userdata) {
|
|
PRINT_NAME();
|
|
|
|
if (callback == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
for (const auto& entry: g_np_state_callbacks) {
|
|
if (entry.callback == callback) {
|
|
return np_error_callback_already_registered;
|
|
}
|
|
}
|
|
g_np_state_callbacks.push_back({callback, userdata, true});
|
|
return static_cast<int>(g_np_state_callbacks.size());
|
|
}
|
|
|
|
void KYTY_SYSV_ABI NpRegisterGamePresenceCallback(void* /*callback*/, void* /*userdata*/) {
|
|
PRINT_NAME();
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpRegisterPlusEventCallback(void* /*callback*/, void* /*userdata*/) {
|
|
PRINT_NAME();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpRegisterPremiumEventCallback(void* /*callback*/, void* /*userdata*/) {
|
|
PRINT_NAME();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpRegisterNpReachabilityStateCallback(void* /*callback*/, void* /*userdata*/) {
|
|
PRINT_NAME();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpGetNpId(int user_id, NpId* np_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
EXIT_NOT_IMPLEMENTED(np_id == nullptr);
|
|
|
|
// int s = snprintf(np_id->handle.data, 16, "Kyty");
|
|
// EXIT_NOT_IMPLEMENTED(s >= 16);
|
|
// np_id->handle.term = 0;
|
|
std::memset(np_id, 0, sizeof(*np_id));
|
|
|
|
// return OK;
|
|
return np_error_signed_out;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpGetOnlineId(int user_id, NpOnlineId* online_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
EXIT_NOT_IMPLEMENTED(online_id == nullptr);
|
|
|
|
// int s = snprintf(online_id->data, 16, "Kyty");
|
|
// EXIT_NOT_IMPLEMENTED(s >= 16);
|
|
// online_id->term = 0;
|
|
std::memset(online_id, 0, sizeof(*online_id));
|
|
|
|
// return OK;
|
|
return np_error_signed_out;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpGetAccountIdA(int user_id, uint64_t* account_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
EXIT_NOT_IMPLEMENTED(account_id == nullptr);
|
|
|
|
// *account_id = 0x00000000feedfaceull;
|
|
*account_id = 0;
|
|
|
|
// return OK;
|
|
return np_error_signed_out;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpGetAccountCountryA(int user_id, void* country_code) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
EXIT_NOT_IMPLEMENTED(country_code == nullptr);
|
|
|
|
auto* code = static_cast<NpCountryCode*>(country_code);
|
|
std::memset(code, 0, sizeof(*code));
|
|
// code->data[0] = '';
|
|
// code->data[1] = 'S';
|
|
|
|
// return OK;
|
|
return np_error_signed_out;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpGetAccountAge(int req_id, int user_id, uint8_t* age) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
LOGF("\t age = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(age));
|
|
|
|
if (req_id <= 0 || age == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
// *age = 21;
|
|
*age = 0;
|
|
|
|
// return OK;
|
|
return np_error_signed_out;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpCreateRequest() {
|
|
PRINT_NAME();
|
|
|
|
const auto req_id = np_create_request(false);
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
|
|
return req_id;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpCreateAsyncRequest(const NpCreateAsyncRequestParameter* param) {
|
|
PRINT_NAME();
|
|
|
|
if (param == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
if (param->size < sizeof(NpCreateAsyncRequestParameter)) {
|
|
return np_error_invalid_size;
|
|
}
|
|
|
|
LOGF("\t size = %" PRIu64 "\n"
|
|
"\t cpu_affinity_mask = %" PRIu64 "\n"
|
|
"\t thread_priority = %d\n",
|
|
param->size, param->cpu_affinity_mask, param->thread_priority);
|
|
|
|
const auto req_id = np_create_request(true);
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
|
|
return req_id;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpDeleteRequest(int req_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
|
|
std::lock_guard lock(g_np_request_mutex);
|
|
|
|
auto* request = np_get_request_locked(req_id);
|
|
if (request == nullptr) {
|
|
return np_error_request_not_found;
|
|
}
|
|
|
|
request->state = NpRequestState::Free;
|
|
request->async = false;
|
|
request->result = OK;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpAbortRequest(int req_id) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
|
|
std::lock_guard lock(g_np_request_mutex);
|
|
|
|
auto* request = np_get_request_locked(req_id);
|
|
if (request == nullptr) {
|
|
return np_error_request_not_found;
|
|
}
|
|
|
|
request->state = NpRequestState::Aborted;
|
|
request->result = np_error_aborted;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpCheckNpAvailability(int req_id, const char* user, void* result) {
|
|
PRINT_NAME();
|
|
|
|
// EXIT_NOT_IMPLEMENTED(req_id <= 0);
|
|
// EXIT_NOT_IMPLEMENTED(user == nullptr);
|
|
// EXIT_NOT_IMPLEMENTED(result != nullptr);
|
|
|
|
LOGF("\t req_id = %d\n"
|
|
"\t user = %s\n",
|
|
req_id, user != nullptr ? user : "(null)");
|
|
|
|
if (req_id <= 0 || user == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
if (result != nullptr) {
|
|
std::memset(result, 0, sizeof(int));
|
|
}
|
|
|
|
std::lock_guard lock(g_np_request_mutex);
|
|
|
|
auto* request = np_get_request_locked(req_id);
|
|
if (request == nullptr) {
|
|
return np_error_request_not_found;
|
|
}
|
|
|
|
// return OK;
|
|
return np_complete_signed_out_locked(request);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpCheckNpReachability(int req_id, int user_id) {
|
|
PRINT_NAME();
|
|
|
|
// EXIT_NOT_IMPLEMENTED(req_id <= 0);
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
if (req_id <= 0) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
std::lock_guard lock(g_np_request_mutex);
|
|
|
|
auto* request = np_get_request_locked(req_id);
|
|
if (request == nullptr) {
|
|
return np_error_request_not_found;
|
|
}
|
|
|
|
// return OK;
|
|
return np_complete_signed_out_locked(request);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpPollAsync(int req_id, int* result) {
|
|
PRINT_NAME();
|
|
|
|
if (result == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
|
|
std::lock_guard lock(g_np_request_mutex);
|
|
|
|
auto* request = np_get_request_locked(req_id);
|
|
if (request == nullptr) {
|
|
return np_error_request_not_found;
|
|
}
|
|
|
|
if (!request->async || request->state == NpRequestState::Ready) {
|
|
return np_error_invalid_id;
|
|
}
|
|
|
|
*result = request->result;
|
|
|
|
// return request->state == NpRequestState::Aborted ? np_error_aborted : OK;
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpCheckPremium(int req_id, const NpCheckPremiumParameter* param,
|
|
NpCheckPremiumResult* result) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t req_id = %d\n", req_id);
|
|
LOGF("\t param = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(param));
|
|
LOGF("\t result = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(result));
|
|
|
|
if (param == nullptr || result == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
if (param->size < sizeof(NpCheckPremiumParameter)) {
|
|
return np_error_invalid_size;
|
|
}
|
|
|
|
LOGF("\t size = %" PRIu64 "\n"
|
|
"\t user_id = %d\n"
|
|
"\t features = 0x%016" PRIx64 "\n",
|
|
param->size, param->user_id, param->features);
|
|
|
|
std::lock_guard lock(g_np_request_mutex);
|
|
|
|
auto* request = np_get_request_locked(req_id);
|
|
if (request == nullptr) {
|
|
return np_error_request_not_found;
|
|
}
|
|
|
|
if (request->state == NpRequestState::Complete) {
|
|
request->result = np_error_invalid_argument;
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
if (request->state == NpRequestState::Aborted) {
|
|
return np_error_aborted;
|
|
}
|
|
|
|
std::memset(result, 0, sizeof(*result));
|
|
// result->authorized = true;
|
|
|
|
// request->state = NpRequestState::Complete;
|
|
// request->result = OK;
|
|
|
|
// return OK;
|
|
return np_complete_signed_out_locked(request);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpGetState(int user_id, uint32_t* state) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(state == nullptr);
|
|
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
*state = np_state_signed_out;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpGetNpReachabilityState(int user_id, uint32_t* state) {
|
|
PRINT_NAME();
|
|
|
|
if (state == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
// *state = 2; // SCE_NP_REACHABILITY_STATE_REACHABLE
|
|
*state = 0; // SCE_NP_REACHABILITY_STATE_UNAVAILABLE
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI NpHasSignedUp(int user_id, bool* has_signed_up) {
|
|
PRINT_NAME();
|
|
|
|
if (has_signed_up == nullptr) {
|
|
return np_error_invalid_argument;
|
|
}
|
|
|
|
LOGF("\t user_id = %d\n", user_id);
|
|
|
|
*has_signed_up = false;
|
|
|
|
return OK;
|
|
}
|
|
|
|
} // namespace NpManager
|
|
|
|
} // namespace Libs::Network
|