net: Implement socket features necessary for Adhoc.

modules/SceNet: Fix and implement several functions.
- Implement abort() and shutdown().
- Fix all error returns by using user error codes instead of kernel error codes.
- Store errno in TLS and introduce macro for returning errors.
- Fix accept() to emplace the new socket correctly and return proper errors.
- Add lock guard in SocketClose to protect socket removal.
- Refactor SceNetEpollControl with store socket as weak_ptr on add to avoid dangling references and clean up unnecessary code in del and mod operations.
- Correctly handle SO_ONESBCAST option support to send on original broadcast address in sceNetSendto.
modules/SceNetCtl: Improve and implement some functions.
- Implement AdhocGetInAddr.
- Implement GetPeerList with peer exchange thread created during Init.
- Add support for returning real IP and NetMask in InetGetInfo.
- Detect and update IP and broadcast addresses upon network interface change when restarting adhoc mode.
- Fix handle adhoc callback.
net/p2psocket: Add required functions for P2P support.
- Reuse Posix functions where possible, using convertP2PToPosix and convertPosixToP2P to correctly handle port/vport.
net/posixsocket: Some fixes, refactorings, and implementations.
- Add support for flags.
- Handle SCE_NET_MSG_DONTWAIT in (recv/send)_packet on Windows with using select() func.
- Fix handling of SCE_NET_SO_ONESBCAST to support proper broadcast address translation on send.
- Fix setsockopt for SO_(RCV/SND)TIMEO on Windows/Linux.
net/epoll: Avoid calling select() when no sockets are monitored.
- Add error translation for select().
- Refactor wait to use weak pointers with a lambda, ensuring sockets are valid for select and not deleted before EpollControl DEL calls.
modules/SceCommonDialog: Launch adhoc peer thread during NetCheckDialogInit in adhoc mode
- Set dialog status to RUNNING during adhoc connection initialization

Co-authored-by: bookmist <roshst@yandex.ru>
Co-authored-by: EXtremeExploit <pedro.montes.alcalde@gmail.com>
Co-authored-by: Narr the Reg <5944268+german77@users.noreply.github.com>
This commit is contained in:
Zangetsu38
2024-12-18 22:19:23 +01:00
committed by bookmist
parent ae8607435f
commit 33e7999309
18 changed files with 966 additions and 287 deletions
+5
View File
@@ -92,6 +92,10 @@ struct SavedataState {
bool draw_info_window = false;
};
struct NetcheckState {
SceNetCheckDialogMode mode = SCE_NETCHECK_DIALOG_MODE_INVALID;
};
struct DialogState {
DialogLangState lang;
DialogType type = NO_DIALOG;
@@ -100,6 +104,7 @@ struct DialogState {
SceCommonDialogResult result = SCE_COMMON_DIALOG_RESULT_OK;
ImeState ime;
MsgState msg;
NetcheckState netcheck;
TrophyState trophy;
SavedataState savedata;
};
+71
View File
@@ -18,6 +18,9 @@
#pragma once
#include <emuenv/app_util.h>
#include <np/common.h>
#include <mem/ptr.h>
#define SCE_SAVEDATA_DIALOG_ERROR_PARAM 0x80100b01
@@ -326,6 +329,74 @@ struct SceMsgDialogParam {
SceChar8 reserved[32];
};
enum SceNetCheckDialogMode : SceInt32 {
SCE_NETCHECK_DIALOG_MODE_INVALID = 0, // Invalid Mode
SCE_NETCHECK_DIALOG_MODE_ADHOC_CONN = 1, // Adhoc Mode
SCE_NETCHECK_DIALOG_MODE_PSN = 2, // PSN Mode
SCE_NETCHECK_DIALOG_MODE_PSN_ONLINE = 3, // PSN Online Mode
SCE_NETCHECK_DIALOG_MODE_PS3_CONNECT = 4, // PS3 Connect Mode
SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_CONN = 5, // PSP Adhoc Connect Mode
SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_CREATE = 6, // PSP Adhoc Create Mode
SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_JOIN = 7 // PSP Adhoc Join Mode
};
enum SceNetCheckDialogPS3ConnectAction : SceInt32 {
SCE_NETCHECK_DIALOG_PS3_CONNECT_ACTION_ENTER = 0, // Connect to PS3
SCE_NETCHECK_DIALOG_PS3_CONNECT_ACTION_LEAVE = 1 // Disconnect from PS3
};
#define SCE_NET_CTL_CNF_NAME_LEN (64 + 1)
#define SCE_NET_CTL_SSID_LEN (32 + 1)
#define SCE_NET_CTL_WIFI_SECURITY_KEY_LEN (64 + 1)
#define SCE_NET_CTL_AUTH_NAME_LEN (127 + 1)
#define SCE_NET_CTL_AUTH_KEY_LEN (127 + 1)
#define SCE_NET_CTL_HOSTNAME_LEN (255 + 1)
#define SCE_NET_CTL_IPV4_ADDR_STR_LEN 16
struct SceNetCheckDialogPS3ConnectParam {
SceNetCheckDialogPS3ConnectAction action;
char ssid[SCE_NET_CTL_SSID_LEN];
char wpaKey[SCE_NET_CTL_WIFI_SECURITY_KEY_LEN];
char titleId[9 + 1];
};
#define SCE_NETCHECK_DIALOG_INITIAL_AGE_RESTRICTION (-1) // (initial) default age restriction
#define SCE_NETCHECK_DIALOG_COUNTRY_CODE_LEN (2) // country code length
#define SCE_NETCHECK_DIALOG_AGE_RESTRICTION_COUNT_MAX (200) // The maximum number of country-specific age restriction
struct SceNetCheckDialogAgeRestriction {
char countryCode[SCE_NETCHECK_DIALOG_COUNTRY_CODE_LEN]; // country code
SceInt8 age; // age
SceInt8 padding; // Padding for alignment
};
#define SCE_NET_ADHOCCTL_GROUPNAME_LEN 8
struct SceNetAdhocctlGroupName {
SceChar8 data[SCE_NET_ADHOCCTL_GROUPNAME_LEN];
};
struct SceNetCheckDialogParam {
SceUInt32 sdkVersion; // internal range
SceCommonDialogParam commonParam; // common dialog parameter
SceNetCheckDialogMode mode; // dialog mode
np::CommunicationID npCommunicationId; // Communication ID
Ptr<SceNetCheckDialogPS3ConnectParam> ps3ConnectParam; // PS3 connect mode parameter
Ptr<SceNetAdhocctlGroupName> groupName; // PSP Adhoc connect Group name parameter
SceUInt32 timeoutUs; // PSP Adhoc join timeout parameter
SceInt8 defaultAgeRestriction; // default age restriction parameter
SceInt8 padding[3]; // Padding for alignment
SceInt32 ageRestrictionCount; // Number of country-specific age restriction
Ptr<const SceNetCheckDialogAgeRestriction> ageRestriction; // country-specific age restriction list parameter
SceUInt8 reserved[104]; // reserved
};
struct SceNetCheckDialogResult {
SceInt32 result;
SceBool psnModeSucceeded;
SceUInt8 reserved[124];
};
struct SceSaveDataDialogFixedParam {
SceAppUtilSaveDataSlot targetSlot;
SceChar8 reserved[32];
+1 -1
View File
@@ -15,7 +15,7 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <module/module.h>
#include "SceAppUtil.h"
#include <emuenv/app_util.h>
+22
View File
@@ -0,0 +1,22 @@
// Vita3K emulator project
// Copyright (C) 2025 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <module/module.h>
DECL_EXPORT(int, sceAppUtilSystemParamGetString, unsigned int paramId, SceChar8 *buf, SceSize bufSize);
@@ -24,6 +24,7 @@
#include <io/device.h>
#include <io/functions.h>
#include <io/vfs.h>
#include <net/state.h>
#include <packages/functions.h>
#include <util/log.h>
#include <util/string_utils.h>
@@ -491,16 +492,14 @@ EXPORT(int, sceNetCheckDialogGetPS3ConnectInfo) {
return UNIMPLEMENTED();
}
typedef struct SceNetCheckDialogResult {
SceInt32 result;
SceBool psnModeSucceeded;
SceUInt8 reserved[124];
} SceNetCheckDialogResult;
EXPORT(int, sceNetCheckDialogGetResult, SceNetCheckDialogResult *result) {
TRACY_FUNC(sceNetCheckDialogGetResult, result);
result->result = 0;
return STUBBED("result->result = 0");
result->result = emuenv.common_dialog.result;
if (emuenv.common_dialog.netcheck.mode != SCE_NETCHECK_DIALOG_MODE_ADHOC_CONN)
STUBBED("result->result = 0");
return 0;
}
EXPORT(SceCommonDialogStatus, sceNetCheckDialogGetStatus) {
@@ -508,14 +507,34 @@ EXPORT(SceCommonDialogStatus, sceNetCheckDialogGetStatus) {
if (emuenv.common_dialog.type != NETCHECK_DIALOG)
return SCE_COMMON_DIALOG_STATUS_NONE;
STUBBED("SCE_COMMON_DIALOG_STATUS_FINISHED");
if (emuenv.common_dialog.netcheck.mode != SCE_NETCHECK_DIALOG_MODE_ADHOC_CONN)
STUBBED("SCE_COMMON_DIALOG_STATUS_FINISHED");
return emuenv.common_dialog.status;
}
EXPORT(int, sceNetCheckDialogInit) {
EXPORT(int, sceNetCheckDialogInit, const SceNetCheckDialogParam *param) {
TRACY_FUNC(sceNetCheckDialogInit);
if (emuenv.common_dialog.type != NO_DIALOG)
return RET_ERROR(SCE_COMMON_DIALOG_ERROR_BUSY);
emuenv.common_dialog.type = NETCHECK_DIALOG;
emuenv.common_dialog.status = SCE_COMMON_DIALOG_STATUS_FINISHED;
emuenv.common_dialog.netcheck.mode = param->mode;
switch (param->mode) {
case SCE_NETCHECK_DIALOG_MODE_ADHOC_CONN:
LOG_INFO("Triggering adhoc thread");
emuenv.netctl.adhocCondVarReady = true;
emuenv.netctl.adhocCondVar.notify_all();
emuenv.netctl.adhocState = SCE_NET_CTL_STATE_CONNECTING;
emuenv.common_dialog.status = SCE_COMMON_DIALOG_STATUS_RUNNING;
break;
default:
emuenv.common_dialog.status = SCE_COMMON_DIALOG_STATUS_FINISHED;
emuenv.common_dialog.result = SCE_COMMON_DIALOG_RESULT_OK;
break;
}
return UNIMPLEMENTED();
}
@@ -523,6 +542,7 @@ EXPORT(int, sceNetCheckDialogTerm) {
TRACY_FUNC(sceNetCheckDialogTerm);
emuenv.common_dialog.status = SCE_COMMON_DIALOG_STATUS_NONE;
emuenv.common_dialog.type = NO_DIALOG;
emuenv.common_dialog.netcheck.mode = SCE_NETCHECK_DIALOG_MODE_INVALID;
return UNIMPLEMENTED();
}
+127 -117
View File
@@ -17,13 +17,15 @@
#include "SceNet.h"
#include <cstdio>
#include <kernel/state.h>
#include <net/state.h>
#include <net/types.h>
#include <util/lock_and_find.h>
#include <util/net_utils.h>
#include <chrono>
#include <cstdio>
#include <thread>
#include <util/tracy.h>
@@ -110,28 +112,46 @@ std::string to_debug_str<SceNetSocketOption>(const MemState &mem, SceNetSocketOp
return std::to_string(type);
}
static int ret_net_errno(EmuEnvState &emuenv, int thread_id, int ret) {
if (ret < 0) {
auto addr = emuenv.kernel.get_thread_tls_addr(emuenv.mem, thread_id, TLS_NET_ERRNO);
if (addr) {
auto inner_ptr = addr.get(emuenv.mem);
if (inner_ptr)
*reinterpret_cast<int *>(inner_ptr) = ret & 0xff;
}
}
return ret;
}
#define RET_NET_ERRNO(ret) \
do { \
int _r = ret_net_errno(emuenv, thread_id, (ret)); \
return (_r < 0 ? RET_ERROR(_r) : _r); \
} while (0)
EXPORT(int, sceNetAccept, int sid, SceNetSockaddr *addr, unsigned int *addrlen) {
TRACY_FUNC(sceNetAccept, sid, addr, addrlen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_EBADF);
}
auto newsock = sock->accept(addr, addrlen);
if (!newsock) {
return RET_ERROR(-1);
}
if (!sock)
RET_NET_ERRNO(SCE_NET_ERROR_EBADF);
int err = 0;
auto newsock = sock->accept(addr, addrlen, err);
if (!newsock)
RET_NET_ERRNO(err);
auto id = ++emuenv.net.next_id;
emuenv.net.socks.emplace(id, sock);
emuenv.net.socks.emplace(id, newsock);
return id;
}
EXPORT(int, sceNetBind, int sid, const SceNetSockaddr *addr, unsigned int addrlen) {
TRACY_FUNC(sceNetBind, sid, addr, addrlen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_EBADF);
}
return sock->bind(addr, addrlen);
RET_NET_ERRNO(sock ? sock->bind(addr, addrlen) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetClearDnsCache) {
@@ -142,10 +162,8 @@ EXPORT(int, sceNetClearDnsCache) {
EXPORT(int, sceNetConnect, int sid, const SceNetSockaddr *addr, unsigned int addrlen) {
TRACY_FUNC(sceNetConnect, sid, addr, addrlen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_EBADF);
}
return sock->connect(addr, addrlen);
RET_NET_ERRNO(sock ? sock->connect(addr, addrlen) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetDumpAbort) {
@@ -185,34 +203,26 @@ EXPORT(int, sceNetEpollAbort) {
EXPORT(int, sceNetEpollControl, int eid, SceNetEpollControlFlag op, int id, SceNetEpollEvent *ev) {
TRACY_FUNC(sceNetEpollControl, eid, op, id, ev);
auto epoll = lock_and_find(eid, emuenv.net.epolls, emuenv.kernel.mutex);
if (!epoll) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
if (!epoll)
RET_NET_ERRNO(SCE_NET_ERROR_EBADF);
if (id == emuenv.net.resolver_id) {
STUBBED("Async DNS resolve is not supported");
return 0;
}
auto sock = lock_and_find(id, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
auto posixSocket = std::dynamic_pointer_cast<PosixSocket>(sock);
if (!posixSocket) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
switch (op) {
case SCE_NET_EPOLL_CTL_ADD:
return epoll->add(id, posixSocket->sock, ev);
case SCE_NET_EPOLL_CTL_ADD: {
const auto sock = lock_and_find(id, emuenv.net.socks, emuenv.kernel.mutex);
RET_NET_ERRNO(sock ? epoll->add(id, sock, ev) : SCE_NET_ERROR_EBADF);
}
case SCE_NET_EPOLL_CTL_DEL:
return epoll->del(id, posixSocket->sock, ev);
RET_NET_ERRNO(epoll->del(id));
case SCE_NET_EPOLL_CTL_MOD:
return epoll->mod(id, posixSocket->sock, ev);
RET_NET_ERRNO(epoll->mod(id, ev));
default:
return RET_ERROR(SCE_NET_ERROR_EINVAL);
RET_NET_ERRNO(SCE_NET_ERROR_EINVAL);
}
}
@@ -227,23 +237,16 @@ EXPORT(int, sceNetEpollCreate, const char *name, int flags) {
EXPORT(int, sceNetEpollDestroy, int eid) {
TRACY_FUNC(sceNetEpollDestroy, eid);
const std::lock_guard<std::mutex> lock(emuenv.kernel.mutex);
if (emuenv.net.epolls.erase(eid) == 0) {
return RET_ERROR(SCE_NET_EBADF);
}
return 0;
RET_NET_ERRNO(emuenv.net.epolls.erase(eid) ? 0 : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetEpollWait, int eid, SceNetEpollEvent *events, int maxevents, int timeout) {
TRACY_FUNC(sceNetEpollWait, eid, events, maxevents, timeout);
auto epoll = lock_and_find(eid, emuenv.net.epolls, emuenv.kernel.mutex);
if (!epoll) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
return epoll->wait(events, maxevents, timeout);
RET_NET_ERRNO(epoll ? epoll->wait(events, maxevents, timeout) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetEpollWaitCB) {
@@ -261,10 +264,10 @@ EXPORT(Ptr<int>, sceNetErrnoLoc) {
EXPORT(int, sceNetEtherNtostr, SceNetEtherAddr *n, char *str, unsigned int len) {
TRACY_FUNC(sceNetEtherNtostr, n, str, len);
if (!emuenv.net.inited)
return RET_ERROR(SCE_NET_ERROR_ENOTINIT);
RET_NET_ERRNO(SCE_NET_ERROR_ENOTINIT);
if (!n || !str || len <= 0x11)
return RET_ERROR(SCE_NET_ERROR_EINVAL);
RET_NET_ERRNO(SCE_NET_ERROR_EINVAL);
snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
n->data[0], n->data[1], n->data[2], n->data[3], n->data[4], n->data[5]);
@@ -274,10 +277,10 @@ EXPORT(int, sceNetEtherNtostr, SceNetEtherAddr *n, char *str, unsigned int len)
EXPORT(int, sceNetEtherStrton, const char *str, SceNetEtherAddr *n) {
TRACY_FUNC(sceNetEtherStrton, str, n);
if (!emuenv.net.inited)
return RET_ERROR(SCE_NET_ERROR_ENOTINIT);
RET_NET_ERRNO(SCE_NET_ERROR_ENOTINIT);
if (!str || !n)
return RET_ERROR(SCE_NET_ERROR_EINVAL);
RET_NET_ERRNO(SCE_NET_ERROR_EINVAL);
sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&n->data[0], &n->data[1], &n->data[2], &n->data[3], &n->data[4], &n->data[5]);
@@ -288,16 +291,15 @@ EXPORT(int, sceNetEtherStrton, const char *str, SceNetEtherAddr *n) {
EXPORT(int, sceNetGetMacAddress, SceNetEtherAddr *addr, int flags) {
TRACY_FUNC(sceNetGetMacAddress, addr, flags);
if (addr == nullptr) {
return RET_ERROR(SCE_NET_EINVAL);
RET_NET_ERRNO(SCE_NET_ERROR_EINVAL);
}
#ifdef _WIN32
IP_ADAPTER_INFO AdapterInfo[16];
DWORD dwBufLen = sizeof(AdapterInfo);
if (GetAdaptersInfo(AdapterInfo, &dwBufLen) != ERROR_SUCCESS) {
return RET_ERROR(SCE_NET_EINVAL);
} else {
if (GetAdaptersInfo(AdapterInfo, &dwBufLen) != ERROR_SUCCESS)
RET_NET_ERRNO(SCE_NET_ERROR_EINVAL);
else
memcpy(addr->data, AdapterInfo[0].Address, 6);
}
#else
// TODO: Implement the function for non Windows OS
return UNIMPLEMENTED();
@@ -328,20 +330,15 @@ EXPORT(int, sceNetGetpeername) {
EXPORT(int, sceNetGetsockname, int sid, SceNetSockaddr *name, unsigned int *namelen) {
TRACY_FUNC(sceNetGetsockname, sid, name, namelen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
return sock->get_socket_address(name, namelen);
RET_NET_ERRNO(sock ? sock->get_socket_address(name, namelen) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetGetsockopt, int sid, int level, int optname, void *optval, unsigned int *optlen) {
TRACY_FUNC(sceNetGetsockopt, sid, level, optname, optval, optlen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
return sock->get_socket_options(level, optname, optval, optlen);
RET_NET_ERRNO(sock ? sock->get_socket_options(level, optname, optval, optlen) : SCE_NET_ERROR_EBADF);
}
EXPORT(SceUInt32, sceNetHtonl, SceUInt32 n) {
@@ -368,7 +365,7 @@ EXPORT(Ptr<const char>, sceNetInetNtop, int af, const void *src, Ptr<char> dst,
const char *res = inet_ntop(af, src, dst_ptr, size);
#endif
if (res == nullptr) {
RET_ERROR(0x0);
ret_net_errno(emuenv, thread_id, SCE_NET_ERROR_EAFNOSUPPORT);
return Ptr<char>();
}
return dst;
@@ -376,24 +373,26 @@ EXPORT(Ptr<const char>, sceNetInetNtop, int af, const void *src, Ptr<char> dst,
EXPORT(int, sceNetInetPton, int af, const char *src, void *dst) {
TRACY_FUNC(sceNetInetPton, af, src, dst);
if (af != SCE_NET_AF_INET)
RET_NET_ERRNO(SCE_NET_ERROR_EAFNOSUPPORT);
#ifdef _WIN32
int res = InetPton(af, src, dst);
#else
int res = inet_pton(af, src, dst);
#endif
if (res < 0) {
return RET_ERROR(-1);
}
return res;
RET_NET_ERRNO(res == 0 ? SCE_NET_ERROR_EINVAL : PosixSocket::translate_return_value(res));
}
EXPORT(int, sceNetInit, SceNetInitParam *param) {
TRACY_FUNC(sceNetInit, param);
if (emuenv.net.inited)
return RET_ERROR(SCE_NET_ERROR_EBUSY);
RET_NET_ERRNO(SCE_NET_ERROR_EBUSY);
if (!param || !param->memory.address() || param->size < 0x4000 || param->flags != 0)
return RET_ERROR(SCE_NET_ERROR_EINVAL);
RET_NET_ERRNO(SCE_NET_ERROR_EINVAL);
#ifdef _WIN32
WORD versionWanted = MAKEWORD(2, 2);
@@ -403,6 +402,8 @@ EXPORT(int, sceNetInit, SceNetInitParam *param) {
emuenv.net.state = 0;
emuenv.net.inited = true;
emuenv.net.resolver_id = ++emuenv.net.next_id;
net_utils::init_address(emuenv.cfg.adhoc_addr, emuenv.net.netAddr, emuenv.net.broadcastAddr);
emuenv.net.current_addr_index = emuenv.cfg.adhoc_addr;
return 0;
}
@@ -410,9 +411,9 @@ EXPORT(int, sceNetListen, int sid, int backlog) {
TRACY_FUNC(sceNetListen, sid, backlog);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
RET_NET_ERRNO(SCE_NET_ERROR_EBADF);
}
return sock->listen(backlog);
RET_NET_ERRNO(sock->listen(backlog));
}
EXPORT(SceUInt32, sceNetNtohl, SceUInt32 n) {
@@ -433,19 +434,15 @@ EXPORT(SceUInt16, sceNetNtohs, SceUInt16 n) {
EXPORT(int, sceNetRecv, int sid, void *buf, unsigned int len, int flags) {
TRACY_FUNC(sceNetRecv, sid, buf, len, flags);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
return sock->recv_packet(buf, len, flags, nullptr, 0);
RET_NET_ERRNO(sock ? sock->recv_packet(buf, len, flags, nullptr, 0) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetRecvfrom, int sid, void *buf, unsigned int len, int flags, SceNetSockaddr *from, unsigned int *fromlen) {
TRACY_FUNC(sceNetRecvfrom, sid, buf, len, flags, from, fromlen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_ERROR_EBADF);
}
return sock->recv_packet(buf, len, flags, from, fromlen);
RET_NET_ERRNO(sock ? sock->recv_packet(buf, len, flags, from, fromlen) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetRecvmsg) {
@@ -481,12 +478,12 @@ EXPORT(int, sceNetResolverStartAton, int rid, const SceNetInAddr *addr, char *ho
return 0;
}
EXPORT(int, sceNetResolverStartNtoa, int rid, const char *emuenvname, SceNetInAddr *addr, int timeout, int retry, int flags) {
TRACY_FUNC(sceNetResolverStartNtoa, rid, emuenvname, addr, timeout, retry, flags);
struct hostent *resolved = gethostbyname(emuenvname);
EXPORT(int, sceNetResolverStartNtoa, int rid, const char *hostname, SceNetInAddr *addr, int timeout, int retry, int flags) {
TRACY_FUNC(sceNetResolverStartNtoa, rid, hostname, addr, timeout, retry, flags);
struct hostent *resolved = gethostbyname(hostname);
if (resolved == nullptr) {
memset(addr, 0, sizeof(*addr));
return RET_ERROR(-1);
RET_NET_ERRNO(SCE_NET_ERROR_EHOSTUNREACH);
}
memcpy(addr, resolved->h_addr, sizeof(uint32_t));
return 0;
@@ -495,10 +492,8 @@ EXPORT(int, sceNetResolverStartNtoa, int rid, const char *emuenvname, SceNetInAd
EXPORT(int, sceNetSend, int sid, const void *msg, unsigned int len, int flags) {
TRACY_FUNC(sceNetSend, sid, msg, len, flags);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_EBADF);
}
return sock->send_packet(msg, len, flags, nullptr, 0);
RET_NET_ERRNO(sock ? sock->send_packet(msg, len, flags, nullptr, 0) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetSendmsg) {
@@ -509,10 +504,15 @@ EXPORT(int, sceNetSendmsg) {
EXPORT(int, sceNetSendto, int sid, const void *msg, unsigned int len, int flags, const SceNetSockaddr *to, unsigned int tolen) {
TRACY_FUNC(sceNetSendto, sid, msg, len, flags, to, tolen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_EBADF);
}
return sock->send_packet(msg, len, flags, to, tolen);
if (!sock)
RET_NET_ERRNO(SCE_NET_ERROR_EBADF);
SceNetSockaddrIn to_in;
std::memcpy(&to_in, to, sizeof(SceNetSockaddrIn));
if (!sock->sockopt_so_onesbcast && (to_in.sin_addr.s_addr == INADDR_BROADCAST))
to_in.sin_addr.s_addr = emuenv.net.broadcastAddr;
RET_NET_ERRNO(sock->send_packet(msg, len, flags, (SceNetSockaddr *)&to_in, tolen));
}
EXPORT(int, sceNetSetDnsInfo) {
@@ -520,17 +520,15 @@ EXPORT(int, sceNetSetDnsInfo) {
return UNIMPLEMENTED();
}
EXPORT(int, sceNetSetsockopt, int sid, SceNetProtocol level, SceNetSocketOption optname, const int *optval, unsigned int optlen) {
TRACY_FUNC(sceNetSetsockopt, sid, level, optname, *optval, optlen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_EBADF);
}
EXPORT(int, sceNetSetsockopt, int sid, SceNetProtocol level, SceNetSocketOption optname, const void *optval, unsigned int optlen) {
TRACY_FUNC(sceNetSetsockopt, sid, level, optname, optval, optlen);
if (optname == 0x40000) {
LOG_ERROR("Unknown socket option {}", log_hex(optname));
return 0;
}
return sock->set_socket_options(level, optname, optval, optlen);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
RET_NET_ERRNO(sock ? sock->set_socket_options(level, optname, optval, optlen) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetShowIfconfig) {
@@ -541,7 +539,7 @@ EXPORT(int, sceNetShowIfconfig) {
EXPORT(int, sceNetShowNetstat) {
TRACY_FUNC(sceNetShowNetstat);
if (!emuenv.net.inited) {
return RET_ERROR(SCE_NET_ERROR_ENOTINIT);
RET_NET_ERRNO(SCE_NET_ERROR_ENOTINIT);
}
return 0;
}
@@ -551,42 +549,54 @@ EXPORT(int, sceNetShowRoute) {
return UNIMPLEMENTED();
}
EXPORT(int, sceNetShutdown) {
TRACY_FUNC(sceNetShutdown);
return UNIMPLEMENTED();
EXPORT(int, sceNetShutdown, int sid, int how) {
TRACY_FUNC(sceNetShutdown, sid, how);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
RET_NET_ERRNO(sock ? sock->shutdown_socket(how) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetSocket, const char *name, int domain, SceNetSocketType type, SceNetProtocol protocol) {
TRACY_FUNC(sceNetSocket, name, domain, type, protocol);
SocketPtr sock;
if (type < SCE_NET_SOCK_STREAM || type > SCE_NET_SOCK_RAW) {
sock = std::make_shared<P2PSocket>(domain, type, protocol);
} else {
sock = std::make_shared<PosixSocket>(domain, type, protocol);
}
bool isP2P = (type == SCE_NET_SOCK_DGRAM_P2P || type == SCE_NET_SOCK_STREAM_P2P);
SocketPtr sock = isP2P ? std::make_shared<P2PSocket>(domain, type, protocol) : std::make_shared<PosixSocket>(domain, type, protocol);
auto id = ++emuenv.net.next_id;
emuenv.net.socks.emplace(id, sock);
return id;
}
EXPORT(int, sceNetSocketAbort) {
TRACY_FUNC(sceNetSocketAbort);
return UNIMPLEMENTED();
EXPORT(int, sceNetSocketAbort, int sid, int flags) {
TRACY_FUNC(sceNetSocketAbort, sid);
if ((sid < 0) || (flags < 0) || (flags > (SCE_NET_SOCKET_ABORT_FLAG_RCV_PRESERVATION | SCE_NET_SOCKET_ABORT_FLAG_SND_PRESERVATION)))
RET_NET_ERRNO(SCE_NET_ERROR_EINVAL);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
RET_NET_ERRNO(sock ? sock->abort(flags) : SCE_NET_ERROR_EBADF);
}
EXPORT(int, sceNetSocketClose, int sid) {
TRACY_FUNC(sceNetSocketClose, sid);
auto sock = lock_and_find(sid, emuenv.net.socks, emuenv.kernel.mutex);
if (!sock) {
return RET_ERROR(SCE_NET_EBADF);
int result = 0;
{
std::lock_guard<std::mutex> lock(emuenv.kernel.mutex);
const auto sock = util::find(sid, emuenv.net.socks);
if (sock) {
result = sock->close();
if (result >= 0)
emuenv.net.socks.erase(sid);
} else
result = SCE_NET_ERROR_EBADF;
}
return sock->close();
RET_NET_ERRNO(result);
}
EXPORT(int, sceNetTerm) {
TRACY_FUNC(sceNetTerm);
if (!emuenv.net.inited) {
return RET_ERROR(SCE_NET_ERROR_ENOTINIT);
RET_NET_ERRNO(SCE_NET_ERROR_ENOTINIT);
}
#ifdef _WIN32
WSACleanup();
+8
View File
@@ -19,4 +19,12 @@
#include <module/module.h>
#include <net/types.h>
DECL_EXPORT(int, sceNetBind, int sid, const SceNetSockaddr *addr, unsigned int addrlen);
DECL_EXPORT(int, sceNetInetPton, int af, const char *src, void *dst);
DECL_EXPORT(int, sceNetSetsockopt, int sid, SceNetProtocol level, SceNetSocketOption optname, const void *optval, unsigned int optlen);
DECL_EXPORT(int, sceNetShutdown, int sid, int how);
DECL_EXPORT(int, sceNetSocket, const char *name, int domain, SceNetSocketType type, SceNetProtocol protocol);
DECL_EXPORT(int, sceNetSocketAbort, int sid, int flags);
DECL_EXPORT(int, sceNetSocketClose, int sid);
+301 -59
View File
@@ -15,13 +15,20 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <../SceAppUtil/SceAppUtil.h>
#include <../SceNet/SceNet.h>
#include <../SceNpManager/SceNpManager.h>
#include <module/module.h>
#include <dialog/state.h>
#include <kernel/state.h>
#include <net/state.h>
#include <net/types.h>
#include <packages/sfo.h>
#include <rtc/rtc.h>
#include <util/lock_and_find.h>
#include <util/net_utils.h>
#include <util/tracy.h>
TRACY_MODULE_NAME(SceNetCtl);
@@ -63,13 +70,6 @@ enum {
SCE_NET_CTL_ERROR_UNKNOWN_DEVICE = 0x80412188
};
enum SceNetCtlState {
SCE_NETCTL_STATE_DISCONNECTED,
SCE_NETCTL_STATE_CONNECTING,
SCE_NETCTL_STATE_FINALIZING,
SCE_NETCTL_STATE_CONNECTED
};
enum SceNetCtlInfoType {
SCE_NETCTL_INFO_GET_CNF_NAME = 1,
SCE_NETCTL_INFO_GET_DEVICE,
@@ -136,15 +136,243 @@ struct SceNetCtlIfStat {
SceUInt32 reserved[8];
};
struct SceNetCtlAdhocPeerInfo;
static void adhoc_thread(EmuEnvState &emuenv, int thread_id) {
LOG_INFO("Adhoc thread started");
constexpr uint16_t AUTH_RECV_VPORT = 0x8235;
constexpr uint16_t AUTH_SEND_VPORT = 0x8236;
const auto handle_error_and_disconnect = [&](int recv_id, int send_id = 0) {
// Close the sockets if they are valid
if (recv_id > 0)
CALL_EXPORT(sceNetSocketClose, recv_id);
if (send_id > 0)
CALL_EXPORT(sceNetSocketClose, send_id);
{
// Set the adhoc state and event to disconnected and notify the condition variable
std::lock_guard<std::mutex> lock(emuenv.netctl.mutex);
emuenv.netctl.adhocState = SCE_NET_CTL_STATE_DISCONNECTED;
emuenv.netctl.adhocEvent = SCE_NET_CTL_EVENT_TYPE_DISCONNECTED;
emuenv.netctl.adhocCondVarReady = false;
emuenv.netctl.adhocCondVar.notify_all();
}
// Set the common dialog status to finished with user canceled result
if (emuenv.common_dialog.status == SCE_COMMON_DIALOG_STATUS_RUNNING) {
emuenv.common_dialog.status = SCE_COMMON_DIALOG_STATUS_FINISHED;
emuenv.common_dialog.result = SCE_COMMON_DIALOG_RESULT_USER_CANCELED;
}
LOG_ERROR("Adhoc thread: error occurred, closing sockets");
};
while (emuenv.netctl.adhocThreadRun.load()) {
{
std::unique_lock<std::mutex> lock(emuenv.netctl.mutex);
emuenv.netctl.adhocCondVar.wait(lock, [&emuenv] {
return emuenv.netctl.adhocCondVarReady || !emuenv.netctl.adhocThreadRun;
});
if (!emuenv.netctl.adhocThreadRun)
break;
}
LOG_INFO("Adhoc thread: starting network loop");
const SceNetSockaddrIn recvBind{
.sin_len = sizeof(SceNetSockaddrIn),
.sin_family = AF_INET,
.sin_port = htons(SCE_NET_ADHOC_PORT),
.sin_addr{ .s_addr = htonl(INADDR_ANY) },
.sin_vport = htons(AUTH_RECV_VPORT),
};
const auto recv_id = CALL_EXPORT(sceNetSocket, "SceNetAdhocAuthRecv", SCE_NET_AF_INET, SCE_NET_SOCK_DGRAM_P2P, SCE_NET_IPPROTO_IP);
if (recv_id < 0) {
LOG_ERROR("Failed to create adhoc recv socket: {}", log_hex(recv_id));
handle_error_and_disconnect(recv_id);
continue;
}
constexpr uint32_t timeout = 100000;
if (CALL_EXPORT(sceNetSetsockopt, recv_id, SCE_NET_SOL_SOCKET, SCE_NET_SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
LOG_ERROR("Failed to set recv timeout on adhoc socket: {}", log_hex(errno));
handle_error_and_disconnect(recv_id);
continue;
}
if (CALL_EXPORT(sceNetBind, recv_id, (SceNetSockaddr *)&recvBind, sizeof(recvBind)) < 0) {
LOG_ERROR("Failed to bind adhoc recv socket: {}", log_hex(errno));
handle_error_and_disconnect(recv_id);
continue;
}
const auto send_id = CALL_EXPORT(sceNetSocket, "SceNetAdhocAuthSend", SCE_NET_AF_INET, SCE_NET_SOCK_DGRAM_P2P, SceNetProtocol(0));
if (send_id < 0) {
LOG_ERROR("Failed to create adhoc send socket: {}", log_hex(send_id));
handle_error_and_disconnect(recv_id, send_id);
continue;
}
const auto val = 1;
if (CALL_EXPORT(sceNetSetsockopt, send_id, SCE_NET_SOL_SOCKET, SCE_NET_SO_BROADCAST, &val, sizeof(val)) < 0) {
LOG_ERROR("Failed to set send timeout on adhoc socket: {}", log_hex(errno));
handle_error_and_disconnect(recv_id, send_id);
continue;
}
SceNetSockaddrIn sendBind{
.sin_len = sizeof(SceNetSockaddrIn),
.sin_family = SCE_NET_AF_INET,
.sin_port = htons(SCE_NET_ADHOC_PORT),
.sin_addr{ .s_addr = htonl(INADDR_ANY) },
.sin_vport = htons(AUTH_SEND_VPORT),
};
if (CALL_EXPORT(sceNetBind, send_id, (SceNetSockaddr *)&sendBind, sizeof(sendBind)) < 0) {
LOG_ERROR("Failed to bind adhoc send socket: {}", log_hex(errno));
handle_error_and_disconnect(recv_id, send_id);
continue;
}
const auto snd_sock = lock_and_find(send_id, emuenv.net.socks, emuenv.netctl.mutex);
if (!snd_sock) {
LOG_ERROR("Failed to find send socket with ID {} in the list", send_id);
handle_error_and_disconnect(recv_id, send_id);
continue;
}
const auto rcv_sock = lock_and_find(recv_id, emuenv.net.socks, emuenv.netctl.mutex);
if (!rcv_sock) {
LOG_ERROR("Failed to find recv socket with ID {} in the list", recv_id);
handle_error_and_disconnect(recv_id, send_id);
continue;
}
if (emuenv.cfg.adhoc_addr != emuenv.net.current_addr_index) {
// If address has changed, reinitialize the network address and broadcast address
std::lock_guard<std::mutex> lock(emuenv.netctl.mutex);
net_utils::init_address(emuenv.cfg.adhoc_addr, emuenv.net.netAddr, emuenv.net.broadcastAddr);
emuenv.net.current_addr_index = emuenv.cfg.adhoc_addr;
}
const SceNetSockaddrIn to{
.sin_len = sizeof(SceNetSockaddrIn),
.sin_family = AF_INET,
.sin_port = htons(SCE_NET_ADHOC_PORT),
.sin_addr{ .s_addr = emuenv.net.broadcastAddr },
.sin_vport = htons(AUTH_RECV_VPORT),
};
// Retrieve the app version from the SFO file
std::string app_ver_string;
int32_t app_ver = 0x00010000; // Default version 1.00
if (sfo::get_data_by_key(app_ver_string, emuenv.sfo_handle, "APP_VER")) {
// Parse version string like "1.05" to uint32_t as 0x00010005
unsigned major = 0, minor = 0;
if (sscanf(app_ver_string.c_str(), "%u.%u", &major, &minor) == 2)
app_ver = (major << 16) | minor;
}
SceNetCtlAdhocPeerInfo selfInfo{
.addr = { .s_addr = emuenv.net.netAddr },
.lastRecv = 0,
.appVer = app_ver,
.isValidNpId = 1,
};
std::vector<int8_t> username(SCE_SYSTEM_PARAM_USERNAME_MAXSIZE);
CALL_EXPORT(sceAppUtilSystemParamGetString, SCE_SYSTEM_PARAM_ID_USER_NAME, username.data(), sizeof(selfInfo.username));
CALL_EXPORT(sceNpManagerGetNpId, &selfInfo.npId);
std::strncpy(selfInfo.username, reinterpret_cast<const char *>(username.data()), sizeof(selfInfo.username) - 1);
selfInfo.username[sizeof(selfInfo.username) - 1] = '\0';
// Notify the common dialog that the adhoc connection is established
emuenv.common_dialog.status = SCE_COMMON_DIALOG_STATUS_FINISHED;
emuenv.common_dialog.result = SCE_COMMON_DIALOG_RESULT_OK;
{
// Set the adhoc event and state to indicate that the connection is established
std::lock_guard<std::mutex> lock(emuenv.netctl.mutex);
emuenv.netctl.adhocState = SCE_NET_CTL_STATE_IPOBTAINED;
emuenv.netctl.adhocEvent = SCE_NET_CTL_EVENT_TYPE_IPOBTAINED;
}
// Timeout for peer inactivity set to 5 seconds, expressed in microseconds
constexpr uint64_t TIMEOUT_USEC = 5'000'000;
// Interval for sending our peer info to the network set to 1 second, expressed in microseconds
constexpr uint64_t SEND_INTERVAL_USEC = 1'000'000;
uint64_t lastSendTicks = rtc_get_ticks(emuenv.kernel.base_tick.tick) - emuenv.kernel.start_tick - SEND_INTERVAL_USEC;
while (emuenv.netctl.adhocCondVarReady.load()) {
const uint64_t currentTicks = rtc_get_ticks(emuenv.kernel.base_tick.tick) - emuenv.kernel.start_tick;
emuenv.netctl.adhocPeers.erase(
std::remove_if(
emuenv.netctl.adhocPeers.begin(),
emuenv.netctl.adhocPeers.end(),
[&](const SceNetCtlAdhocPeerInfo &peer) {
return currentTicks - peer.lastRecv > TIMEOUT_USEC;
}),
emuenv.netctl.adhocPeers.end());
// Send the self info to other peers every 1 second
if ((currentTicks - lastSendTicks) >= SEND_INTERVAL_USEC) {
snd_sock->send_packet(&selfInfo, sizeof(selfInfo), 0, (SceNetSockaddr *)&to, sizeof(to));
lastSendTicks = currentTicks;
}
// Wait for incoming packets
SceNetSockaddrIn fromAddr{};
auto fromlen = uint32_t(sizeof(fromAddr));
SceNetCtlAdhocPeerInfo peerInfo{};
const auto res = rcv_sock->recv_packet(&peerInfo, sizeof(SceNetCtlAdhocPeerInfo), 0, (SceNetSockaddr *)&fromAddr, &fromlen);
if ((res > 0) && (res == sizeof(SceNetCtlAdhocPeerInfo)) && (fromAddr.sin_addr.s_addr != emuenv.net.netAddr)) {
auto peerIt = std::find_if(
emuenv.netctl.adhocPeers.begin(),
emuenv.netctl.adhocPeers.end(),
[&](const SceNetCtlAdhocPeerInfo &peer) {
return peer.addr.s_addr == fromAddr.sin_addr.s_addr;
});
if (peerIt != emuenv.netctl.adhocPeers.end()) {
// If the peer is already in the list, update its lastRecv time
peerIt->lastRecv = currentTicks;
} else {
// Add the peer info to the list if the packet is a valid peer info packet
peerInfo.lastRecv = currentTicks;
emuenv.netctl.adhocPeers.push_back(peerInfo);
}
}
}
// Close the sockets when the loop ends
CALL_EXPORT(sceNetSocketClose, recv_id);
CALL_EXPORT(sceNetSocketClose, send_id);
{
// Notify the event and update the adhoc state
std::lock_guard<std::mutex> lock(emuenv.netctl.mutex);
emuenv.netctl.adhocState = SCE_NET_CTL_STATE_DISCONNECTED;
emuenv.netctl.adhocEvent = SCE_NET_CTL_EVENT_TYPE_DISCONNECT_REQ_FINISHED;
}
LOG_INFO("Adhoc thread: stopping network loop");
}
LOG_INFO("Adhoc thread stopped");
}
EXPORT(int, sceNetCtlAdhocDisconnect) {
TRACY_FUNC(sceNetCtlAdhocDisconnect);
if (!emuenv.netctl.inited) {
if (!emuenv.netctl.inited)
return RET_ERROR(SCE_NET_CTL_ERROR_NOT_INITIALIZED);
}
return UNIMPLEMENTED();
const std::lock_guard<std::mutex> lock(emuenv.netctl.mutex);
emuenv.netctl.adhocPeers.clear();
emuenv.netctl.adhocCondVarReady = false;
emuenv.netctl.adhocCondVar.notify_all();
return STUBBED("Stub");
}
EXPORT(int, sceNetCtlAdhocGetInAddr, SceNetInAddr *inaddr) {
@@ -153,20 +381,29 @@ EXPORT(int, sceNetCtlAdhocGetInAddr, SceNetInAddr *inaddr) {
return RET_ERROR(SCE_NET_CTL_ERROR_NOT_INITIALIZED);
}
if (!inaddr) {
if (!inaddr)
return RET_ERROR(SCE_NET_CTL_ERROR_INVALID_ADDR);
}
return UNIMPLEMENTED();
inaddr->s_addr = emuenv.net.netAddr;
return 0;
}
EXPORT(int, sceNetCtlAdhocGetPeerList, SceSize *peerInfoNum, SceNetCtlAdhocPeerInfo *peerInfo) {
TRACY_FUNC(sceNetCtlAdhocGetPeerList, peerInfoNum, peerInfo);
if (!peerInfoNum) {
return RET_ERROR(SCE_NET_CTL_ERROR_INVALID_ADDR);
}
if (!emuenv.netctl.inited)
return RET_ERROR(SCE_NET_CTL_ERROR_NOT_INITIALIZED);
return UNIMPLEMENTED();
if (!peerInfoNum)
return RET_ERROR(SCE_NET_CTL_ERROR_INVALID_ADDR);
std::lock_guard<std::mutex> lock(emuenv.netctl.mutex);
if (peerInfo)
memcpy(peerInfo, emuenv.netctl.adhocPeers.data(), emuenv.netctl.adhocPeers.size() * sizeof(*peerInfo));
*peerInfoNum = emuenv.netctl.adhocPeers.size();
return 0;
}
EXPORT(int, sceNetCtlAdhocGetResult, int eventType, int *errorCode) {
@@ -185,16 +422,14 @@ EXPORT(int, sceNetCtlAdhocGetResult, int eventType, int *errorCode) {
EXPORT(int, sceNetCtlAdhocGetState, int *state) {
TRACY_FUNC(sceNetCtlAdhocGetState, state);
if (!emuenv.netctl.inited) {
if (!emuenv.netctl.inited)
return RET_ERROR(SCE_NET_CTL_ERROR_NOT_INITIALIZED);
}
if (!state) {
if (!state)
return RET_ERROR(SCE_NET_CTL_ERROR_INVALID_ADDR);
}
*state = SCE_NETCTL_STATE_CONNECTED;
return STUBBED("state = SCE_NETCTL_STATE_CONNECTED");
*state = emuenv.netctl.adhocState;
return 0;
}
EXPORT(int, sceNetCtlAdhocRegisterCallback, Ptr<void> func, Ptr<void> arg, int *cid) {
@@ -250,27 +485,27 @@ EXPORT(int, sceNetCtlCheckCallback) {
return RET_ERROR(SCE_NET_CTL_ERROR_NOT_INITIALIZED);
}
if (emuenv.net.state == 1) {
return 0;
}
emuenv.net.state = 1;
const ThreadStatePtr thread = emuenv.kernel.get_thread(thread_id);
// TODO: Limit the number of callbacks called to 5
// TODO: Check in which order the callbacks are executed
for (auto &callback : emuenv.netctl.callbacks) {
if (callback.pc != 0) {
thread->run_callback(callback.pc, { SCE_NET_CTL_EVENT_TYPE_DISCONNECTED, callback.arg });
// TODO: Check if the network is connected
if (emuenv.net.state != 1) {
for (auto &callback : emuenv.netctl.callbacks) {
if (callback.pc != 0) {
thread->run_callback(callback.pc, { SCE_NET_CTL_EVENT_TYPE_DISCONNECTED, callback.arg });
}
}
emuenv.net.state = 1;
}
for (auto &callback : emuenv.netctl.adhocCallbacks) {
if (callback.pc != 0) {
thread->run_callback(callback.pc, { SCE_NET_CTL_EVENT_TYPE_DISCONNECTED, callback.arg });
// Check if there are any adhoc events to notify
if ((emuenv.netctl.adhocEvent != SCE_NET_CTL_EVENT_TYPE_NONE) && (emuenv.netctl.adhocEvent != emuenv.netctl.lastNotifiedAdhocEvent)) {
for (auto &callback : emuenv.netctl.adhocCallbacks) {
if (callback.pc != 0)
thread->run_callback(callback.pc, { emuenv.netctl.adhocEvent, callback.arg });
}
// Update the last notified adhoc event
emuenv.netctl.lastNotifiedAdhocEvent = emuenv.netctl.adhocEvent;
}
return STUBBED("Stub");
@@ -330,23 +565,9 @@ EXPORT(int, sceNetCtlInetGetInfo, int code, SceNetCtlInfo *info) {
return RET_ERROR(SCE_NET_CTL_ERROR_INVALID_ADDR);
}
const auto addr = net_utils::get_selected_assigned_addr(emuenv.cfg.adhoc_addr);
switch (code) {
case SCE_NETCTL_INFO_GET_IP_ADDRESS: {
strcpy(info->ip_address, "127.0.0.1"); // placeholder in case gethostbyname can't find another ip
char devname[80];
gethostname(devname, 80);
struct hostent *resolved = gethostbyname(devname);
for (int i = 0; resolved->h_addr_list[i] != nullptr; ++i) {
struct in_addr addrIn;
memcpy(&addrIn, resolved->h_addr_list[i], sizeof(uint32_t));
char *addr = inet_ntoa(addrIn);
if (strcmp(addr, "127.0.0.1") != 0) {
strcpy(info->ip_address, addr);
break;
}
}
break;
}
case SCE_NETCTL_INFO_GET_DEVICE:
info->device = 0; /*SCE_NET_CTL_DEVICE_WIRELESS*/
// STUBBED("SCE_NETCTL_INFO_GET_DEVICE return SCE_NET_CTL_DEVICE_WIRELESS");
@@ -355,6 +576,12 @@ EXPORT(int, sceNetCtlInetGetInfo, int code, SceNetCtlInfo *info) {
info->rssi_percentage = 100;
// STUBBED("code SCE_NETCTL_INFO_GET_RSSI_PERCENTAGE return 100%");
break;
case SCE_NETCTL_INFO_GET_IP_ADDRESS:
inet_pton(AF_INET, addr.addr.c_str(), &info->ip_address);
break;
case SCE_NETCTL_INFO_GET_NETMASK:
inet_pton(AF_INET, addr.netMask.c_str(), &info->netmask);
break;
default:
switch (code) {
case SCE_NETCTL_INFO_GET_CNF_NAME:
@@ -393,9 +620,6 @@ EXPORT(int, sceNetCtlInetGetInfo, int code, SceNetCtlInfo *info) {
case SCE_NETCTL_INFO_GET_PPPOE_AUTH_NAME:
STUBBED("code SCE_NETCTL_INFO_GET_PPPOE_AUTH_NAME not implemented");
break;
case SCE_NETCTL_INFO_GET_NETMASK:
STUBBED("code SCE_NETCTL_INFO_GET_NETMASK not implemented");
break;
case SCE_NETCTL_INFO_GET_DEFAULT_ROUTE:
STUBBED("code SCE_NETCTL_INFO_GET_DEFAULT_ROUTE not implemented");
break;
@@ -445,7 +669,7 @@ EXPORT(int, sceNetCtlInetGetState, int *state) {
return RET_ERROR(SCE_NET_CTL_ERROR_INVALID_ADDR);
}
*state = SCE_NETCTL_STATE_CONNECTED;
*state = SCE_NET_CTL_STATE_IPOBTAINED;
return STUBBED("state = SCE_NETCTL_STATE_CONNECTED");
}
@@ -508,6 +732,9 @@ EXPORT(int, sceNetCtlInit) {
emuenv.netctl.callbacks.fill({ 0, 0 });
emuenv.netctl.inited = true;
emuenv.netctl.adhocThreadRun = true;
emuenv.netctl.adhocThread = std::thread(adhoc_thread, std::ref(emuenv), thread_id);
return STUBBED("Stub");
}
@@ -515,4 +742,19 @@ EXPORT(void, sceNetCtlTerm) {
TRACY_FUNC(sceNetCtlTerm);
STUBBED("Stub");
emuenv.netctl.inited = false;
{
const std::lock_guard<std::mutex> lock(emuenv.netctl.mutex);
emuenv.netctl.adhocThreadRun = false;
emuenv.netctl.adhocCondVarReady = false;
emuenv.netctl.adhocCondVar.notify_all();
}
if (emuenv.netctl.adhocThread.joinable())
emuenv.netctl.adhocThread.join();
emuenv.netctl.adhocState = SCE_NET_CTL_STATE_DISCONNECTED;
emuenv.netctl.adhocEvent = SCE_NET_CTL_EVENT_TYPE_NONE;
emuenv.netctl.lastNotifiedAdhocEvent = SCE_NET_CTL_EVENT_TYPE_NONE;
emuenv.netctl.adhocPeers.clear();
}
+1 -1
View File
@@ -15,7 +15,7 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <module/module.h>
#include "SceNpManager.h"
#include "util/types.h"
@@ -0,0 +1,23 @@
// Vita3K emulator project
// Copyright (C) 2025 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <module/module.h>
#include <np/state.h>
DECL_EXPORT(int, sceNpManagerGetNpId, np::SceNpId *id);
+1 -1
View File
@@ -12,7 +12,7 @@ add_library(
)
target_include_directories(net PUBLIC include)
target_link_libraries(net PUBLIC mem)
target_link_libraries(net PUBLIC emuenv mem np)
if (WIN32)
target_link_libraries(net PRIVATE winsock)
endif()
+4 -4
View File
@@ -5,15 +5,15 @@
struct EpollSocket {
unsigned int events;
SceNetEpollData data;
abs_socket sock;
std::weak_ptr<Socket> sock;
};
struct Epoll {
std::map<int, EpollSocket> eventEntries;
int add(int id, abs_socket sock, SceNetEpollEvent *ev);
int del(int id, abs_socket sock, SceNetEpollEvent *ev);
int mod(int id, abs_socket sock, SceNetEpollEvent *ev);
int add(int id, std::weak_ptr<Socket> sock, SceNetEpollEvent *ev);
int del(int id);
int mod(int id, SceNetEpollEvent *ev);
int wait(SceNetEpollEvent *events, int maxevents, int timeout);
};
+16 -11
View File
@@ -45,18 +45,22 @@ struct Socket;
typedef std::shared_ptr<Socket> SocketPtr;
struct Socket {
int sockopt_so_onesbcast = 0;
explicit Socket(int domain, int type, int protocol) {}
virtual ~Socket() = default;
virtual int abort(int flags) = 0;
virtual int close() = 0;
virtual int shutdown_socket(int how) = 0;
virtual int bind(const SceNetSockaddr *addr, unsigned int addrlen) = 0;
virtual int send_packet(const void *msg, unsigned int len, int flags, const SceNetSockaddr *to, unsigned int tolen) = 0;
virtual int recv_packet(void *buf, unsigned int len, int flags, SceNetSockaddr *from, unsigned int *fromlen) = 0;
virtual int set_socket_options(int level, int optname, const void *optval, unsigned int optlen) = 0;
virtual int get_socket_options(int level, int optname, void *optval, unsigned int *optlen) = 0;
virtual int connect(const SceNetSockaddr *addr, unsigned int namelen) = 0;
virtual SocketPtr accept(SceNetSockaddr *addr, unsigned int *addrlen) = 0;
virtual SocketPtr accept(SceNetSockaddr *addr, unsigned int *addrlen, int &err) = 0;
virtual int listen(int backlog) = 0;
virtual int get_socket_address(SceNetSockaddr *name, unsigned int *namelen) = 0;
};
@@ -66,7 +70,6 @@ struct PosixSocket : public Socket {
abs_socket sock;
int sockopt_so_reuseport = 0;
int sockopt_so_onesbcast = 0;
int sockopt_so_usecrypto = 0;
int sockopt_so_usesignature = 0;
int sockopt_so_tppolicy = 0;
@@ -75,6 +78,9 @@ struct PosixSocket : public Socket {
int sockopt_ip_maxttl = 0;
int sockopt_tcp_mss_to_advertise = 0;
int abort_flags = 0;
bool is_aborted = false;
explicit PosixSocket(int domain, int type, int protocol)
: Socket(domain, type, protocol)
, sock(socket(domain, type, protocol)) {}
@@ -83,30 +89,29 @@ struct PosixSocket : public Socket {
: Socket(0, 0, 0)
, sock(sock) {}
static int translate_return_value(int retval);
int abort(int flags) override;
int close() override;
int shutdown_socket(int how) override;
int bind(const SceNetSockaddr *addr, unsigned int addrlen) override;
int send_packet(const void *msg, unsigned int len, int flags, const SceNetSockaddr *to, unsigned int tolen) override;
int recv_packet(void *buf, unsigned int len, int flags, SceNetSockaddr *from, unsigned int *fromlen) override;
int set_socket_options(int level, int optname, const void *optval, unsigned int optlen) override;
int get_socket_options(int level, int optname, void *optval, unsigned int *optlen) override;
int connect(const SceNetSockaddr *addr, unsigned int namelen) override;
SocketPtr accept(SceNetSockaddr *addr, unsigned int *addrlen) override;
SocketPtr accept(SceNetSockaddr *addr, unsigned int *addrlen, int &err) override;
int listen(int backlog) override;
int get_socket_address(SceNetSockaddr *name, unsigned int *namelen) override;
};
struct P2PSocket : public Socket {
explicit P2PSocket(int domain, int type, int protocol)
: Socket(domain, type, protocol) {}
struct P2PSocket : public PosixSocket {
explicit P2PSocket(int domain, int type, int protocol);
int close() override;
int bind(const SceNetSockaddr *addr, unsigned int addrlen) override;
int send_packet(const void *msg, unsigned int len, int flags, const SceNetSockaddr *to, unsigned int tolen) override;
int recv_packet(void *buf, unsigned int len, int flags, SceNetSockaddr *from, unsigned int *fromlen) override;
int set_socket_options(int level, int optname, const void *optval, unsigned int optlen) override;
int get_socket_options(int level, int optname, void *optval, unsigned int *optlen) override;
int connect(const SceNetSockaddr *addr, unsigned int namelen) override;
SocketPtr accept(SceNetSockaddr *addr, unsigned int *addrlen) override;
int listen(int backlog) override;
SocketPtr accept(SceNetSockaddr *addr, unsigned int *addrlen, int &err) override;
int get_socket_address(SceNetSockaddr *name, unsigned int *namelen) override;
};
+11
View File
@@ -36,11 +36,22 @@ struct NetState {
NetEpolls epolls;
int state = -1;
int resolver_id = 0;
int current_addr_index = 0;
uint32_t broadcastAddr = 0xFFFFFFFF;
uint32_t netAddr = 0xFFFFFFFF;
};
struct NetCtlState {
std::array<SceNetCtlCallback, 8> adhocCallbacks;
std::array<SceNetCtlCallback, 8> callbacks;
std::vector<SceNetCtlAdhocPeerInfo> adhocPeers;
bool inited = false;
std::thread adhocThread;
std::atomic<bool> adhocCondVarReady = false;
std::condition_variable adhocCondVar;
SceNetCtlState adhocState = SCE_NET_CTL_STATE_DISCONNECTED;
SceNetCtlEventType adhocEvent = SCE_NET_CTL_EVENT_TYPE_NONE;
SceNetCtlEventType lastNotifiedAdhocEvent = SCE_NET_CTL_EVENT_TYPE_NONE;
std::atomic<bool> adhocThreadRun = false;
std::mutex mutex;
};
+41 -1
View File
@@ -17,7 +17,13 @@
#pragma once
#include <emuenv/app_util.h>
#include <mem/ptr.h>
#include <np/common.h>
#define SCE_NET_ADHOC_PORT 3658
#define SCE_NET_AF_INET 2
// Define our own htonll and ntohll because its not available in some systems/platforms
#define HTONLL(x) ((((uint64_t)htonl((x)&0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32)))
@@ -32,6 +38,20 @@ enum SceNetProtocol : uint32_t {
SCE_NET_SOL_SOCKET = 0xFFFF
};
enum SceNetMsgFlag : uint32_t {
SCE_NET_MSG_PEEK = 0x00000002,
SCE_NET_MSG_WAITALL = 0x00000040,
SCE_NET_MSG_DONTWAIT = 0x00000080,
SCE_NET_MSG_USECRYPTO = 0x00000400,
SCE_NET_MSG_USESIGNATURE = 0x00000800,
SCE_NET_MSG_PEEKLEN = (0x00001000 | SCE_NET_MSG_PEEK)
};
enum SceNetSocketAbortFlag : uint32_t {
SCE_NET_SOCKET_ABORT_FLAG_RCV_PRESERVATION = 0x00000001,
SCE_NET_SOCKET_ABORT_FLAG_SND_PRESERVATION = 0x00000002
};
enum SceNetSocketType : uint32_t {
SCE_NET_SOCK_STREAM = 1,
SCE_NET_SOCK_DGRAM = 2,
@@ -335,12 +355,14 @@ struct SceNetSockaddrIn {
unsigned short int sin_vport;
char sin_zero[6];
};
static_assert(sizeof(SceNetSockaddrIn) == 16, "SceNetSockaddrIn has incorrect size");
struct SceNetSockaddr {
unsigned char sa_len;
unsigned char sa_family;
char sa_data[14];
};
static_assert(sizeof(SceNetSockaddr) == 16, "SceNetSockaddr has incorrect size");
struct SceNetInitParam {
Ptr<void> memory;
@@ -359,8 +381,26 @@ struct SceNetEpollEvent {
SceNetEpollData data;
};
enum SceNetCtlEventType {
struct SceNetCtlAdhocPeerInfo {
SceNetInAddr addr;
np::SceNpId npId;
SceUInt64 lastRecv;
int appVer;
SceBool isValidNpId;
char username[SCE_SYSTEM_PARAM_USERNAME_MAXSIZE];
uint8_t padding[7];
};
enum SceNetCtlEventType : uint32_t {
SCE_NET_CTL_EVENT_TYPE_NONE = 0,
SCE_NET_CTL_EVENT_TYPE_DISCONNECTED = 1,
SCE_NET_CTL_EVENT_TYPE_DISCONNECT_REQ_FINISHED = 2,
SCE_NET_CTL_EVENT_TYPE_IPOBTAINED = 3,
};
enum SceNetCtlState : uint32_t {
SCE_NET_CTL_STATE_DISCONNECTED,
SCE_NET_CTL_STATE_CONNECTING,
SCE_NET_CTL_STATE_IPOBTAINING,
SCE_NET_CTL_STATE_IPOBTAINED
};
+60 -29
View File
@@ -1,6 +1,25 @@
// Vita3K emulator project
// Copyright (C) 2025 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <net/epoll.h>
int Epoll::add(int id, abs_socket sock, SceNetEpollEvent *ev) {
#include <optional>
int Epoll::add(int id, std::weak_ptr<Socket> sock, SceNetEpollEvent *ev) {
if (!eventEntries.try_emplace(id, EpollSocket{ ev->events, ev->data, sock }).second) {
return SCE_NET_ERROR_EEXIST;
}
@@ -8,7 +27,7 @@ int Epoll::add(int id, abs_socket sock, SceNetEpollEvent *ev) {
return 0;
}
int Epoll::del(int id, abs_socket sock, SceNetEpollEvent *ev) {
int Epoll::del(int id) {
if (eventEntries.erase(id) == 0) {
return SCE_NET_ERROR_ENOENT;
}
@@ -16,7 +35,7 @@ int Epoll::del(int id, abs_socket sock, SceNetEpollEvent *ev) {
return 0;
}
int Epoll::mod(int id, abs_socket sock, SceNetEpollEvent *ev) {
int Epoll::mod(int id, SceNetEpollEvent *ev) {
auto it = eventEntries.find(id);
if (it == eventEntries.end()) {
return SCE_NET_ERROR_ENOENT;
@@ -29,11 +48,8 @@ int Epoll::mod(int id, abs_socket sock, SceNetEpollEvent *ev) {
static void add_event_fd_set(fd_set *set, int *maxFd, abs_socket sock) {
FD_SET(sock, set);
#ifndef _WIN32
if (sock > *maxFd) {
if (sock > *maxFd)
*maxFd = sock;
}
#endif
}
int Epoll::wait(SceNetEpollEvent *events, int maxevents, int timeout_microseconds) {
@@ -43,45 +59,60 @@ int Epoll::wait(SceNetEpollEvent *events, int maxevents, int timeout_microsecond
FD_ZERO(&exceptFds);
int maxFd = 0;
for (auto &pair : eventEntries) {
if (pair.second.events & SCE_NET_EPOLLIN) {
add_event_fd_set(&readFds, &maxFd, pair.second.sock);
}
if (pair.second.events & SCE_NET_EPOLLOUT) {
add_event_fd_set(&writeFds, &maxFd, pair.second.sock);
}
if (pair.second.events & SCE_NET_EPOLLERR) {
add_event_fd_set(&exceptFds, &maxFd, pair.second.sock);
}
const auto get_valid_posix_socket = [](const std::weak_ptr<Socket> &weak_sock, int id) -> std::optional<abs_socket> {
const auto sock = weak_sock.lock();
if (!sock)
return std::nullopt;
const auto posixSocket = std::dynamic_pointer_cast<PosixSocket>(sock);
if (!posixSocket)
return std::nullopt;
return posixSocket->sock;
};
for (const auto &[id, entry] : eventEntries) {
const auto sock = get_valid_posix_socket(entry.sock, id);
if (!sock)
continue;
if (entry.events & SCE_NET_EPOLLIN)
add_event_fd_set(&readFds, &maxFd, *sock);
if (entry.events & SCE_NET_EPOLLOUT)
add_event_fd_set(&writeFds, &maxFd, *sock);
if (entry.events & SCE_NET_EPOLLERR)
add_event_fd_set(&exceptFds, &maxFd, *sock);
}
if (maxFd == 0)
return 0;
timeval timeout;
timeout.tv_sec = timeout_microseconds / 1000000;
timeout.tv_usec = timeout_microseconds % 1000000;
auto ret = select(maxFd + 1, &readFds, &writeFds, &exceptFds, &timeout);
if (ret < 0) {
// TODO: translate error code
return -1;
}
if (ret < 0)
return PosixSocket::translate_return_value(ret);
int eventCount = 0;
for (auto &pair : eventEntries) {
for (const auto &[id, entry] : eventEntries) {
unsigned int eventTypes = 0;
if (FD_ISSET(pair.second.sock, &readFds)) {
const auto sock = get_valid_posix_socket(entry.sock, id);
if (!sock)
continue;
if (FD_ISSET(*sock, &readFds))
eventTypes |= SCE_NET_EPOLLIN;
}
if (FD_ISSET(pair.second.sock, &writeFds)) {
if (FD_ISSET(*sock, &writeFds))
eventTypes |= SCE_NET_EPOLLOUT;
}
if (FD_ISSET(pair.second.sock, &exceptFds)) {
if (FD_ISSET(*sock, &exceptFds))
eventTypes |= SCE_NET_EPOLLERR;
}
if (eventTypes != 0 && eventCount < maxevents) {
auto i = eventCount++;
events[i].events = eventTypes;
events[i].data = pair.second.data;
events[i].data = entry.data;
}
}
+66 -19
View File
@@ -16,43 +16,90 @@
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <net/socket.h>
#include <util/bit_cast.h>
int P2PSocket::close() {
return 0;
static SceNetSockaddr convertP2PToPosix(const SceNetSockaddr *addr) {
if (!addr) {
return SceNetSockaddr{};
}
SceNetSockaddrIn result = *reinterpret_cast<const SceNetSockaddrIn *>(addr);
// Convert ports from network to host order
const uint16_t port = ntohs(result.sin_port);
const uint16_t vport = ntohs(result.sin_vport);
// Combine the two ports in host order, then convert back to network order
result.sin_port = htons(port + vport);
result.sin_vport = 0; // Clear virtual port since it's not used in Posix
return std::bit_cast<SceNetSockaddr>(result);
}
int P2PSocket::listen(int backlog) {
return 0;
static SceNetSockaddr convertPosixToP2P(const SceNetSockaddr *addr) {
if (!addr) {
return SceNetSockaddr{};
}
SceNetSockaddrIn result = *reinterpret_cast<const SceNetSockaddrIn *>(addr);
// Attempt to recover original port and virtual port if port was previously combined
const uint16_t port = ntohs(result.sin_port);
if (port > SCE_NET_ADHOC_PORT) {
const uint16_t vport = port - SCE_NET_ADHOC_PORT;
result.sin_port = htons(SCE_NET_ADHOC_PORT);
result.sin_vport = htons(vport);
} else {
result.sin_port = htons(port);
result.sin_vport = 0;
}
return std::bit_cast<SceNetSockaddr>(result);
}
SocketPtr P2PSocket::accept(SceNetSockaddr *addr, unsigned int *addrlen) {
return nullptr;
static int p2pSocketTypeToPosixSocketType(int type) {
int hostSockType;
switch (type) {
case SCE_NET_SOCK_DGRAM_P2P:
hostSockType = SOCK_DGRAM;
break;
case SCE_NET_SOCK_STREAM_P2P:
hostSockType = SOCK_STREAM;
break;
default:
hostSockType = -1;
}
return hostSockType;
}
P2PSocket::P2PSocket(int domain, int type, int protocol)
: PosixSocket(domain, p2pSocketTypeToPosixSocketType(type), protocol){};
SocketPtr P2PSocket::accept(SceNetSockaddr *addr, unsigned int *addrlen, int &err) {
const auto res = PosixSocket::accept(addr, addrlen, err);
*addr = convertPosixToP2P(addr);
return res;
}
int P2PSocket::connect(const SceNetSockaddr *addr, unsigned int namelen) {
return 0;
}
int P2PSocket::set_socket_options(int level, int optname, const void *optval, unsigned int optlen) {
return 0;
}
int P2PSocket::get_socket_options(int level, int optname, void *optval, unsigned int *optlen) {
return 0;
const auto p2p_addr = convertP2PToPosix(addr);
return PosixSocket::connect(&p2p_addr, namelen);
}
int P2PSocket::recv_packet(void *buf, unsigned int len, int flags, SceNetSockaddr *from, unsigned int *fromlen) {
return SCE_NET_ERROR_EAGAIN;
const auto res = PosixSocket::recv_packet(buf, len, flags, from, fromlen);
if ((res > 0) && from)
*from = convertPosixToP2P(from);
return res;
}
int P2PSocket::send_packet(const void *msg, unsigned int len, int flags, const SceNetSockaddr *to, unsigned int tolen) {
return 0;
const auto p2p_to = convertP2PToPosix(to);
return PosixSocket::send_packet(msg, len, flags, &p2p_to, tolen);
}
int P2PSocket::bind(const SceNetSockaddr *addr, unsigned int addrlen) {
return 0;
const auto p2p_addr = convertP2PToPosix(addr);
return PosixSocket::bind(&p2p_addr, addrlen);
}
int P2PSocket::get_socket_address(SceNetSockaddr *name, unsigned int *namelen) {
return 0;
const auto res = PosixSocket::get_socket_address(name, namelen);
*name = convertPosixToP2P(name);
return res;
}
+177 -33
View File
@@ -29,7 +29,7 @@
return SCE_NET_ERROR_##errname;
#endif
static int translate_return_value(int retval) {
int PosixSocket::translate_return_value(int retval) {
if (retval < 0) {
#ifdef _WIN32
switch (WSAGetLastError()) {
@@ -123,29 +123,51 @@ static int translate_return_value(int retval) {
static void convertSceSockaddrToPosix(const SceNetSockaddr *src, sockaddr *dst) {
if (src == nullptr || dst == nullptr)
return;
memset(dst, 0, sizeof(sockaddr));
const SceNetSockaddrIn *src_in = (const SceNetSockaddrIn *)src;
sockaddr_in *dst_in = (sockaddr_in *)dst;
const SceNetSockaddrIn *src_in = reinterpret_cast<const SceNetSockaddrIn *>(src);
sockaddr_in *dst_in = reinterpret_cast<sockaddr_in *>(dst);
memset(dst_in, 0, sizeof(*dst_in));
dst_in->sin_family = src_in->sin_family;
dst_in->sin_port = src_in->sin_port;
memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4);
memcpy(&dst_in->sin_addr, &src_in->sin_addr, sizeof(dst_in->sin_addr));
}
static void convertPosixSockaddrToSce(sockaddr *src, SceNetSockaddr *dst) {
if (src == nullptr || dst == nullptr)
return;
memset(dst, 0, sizeof(SceNetSockaddr));
SceNetSockaddrIn *dst_in = (SceNetSockaddrIn *)dst;
sockaddr_in *src_in = (sockaddr_in *)src;
SceNetSockaddrIn *dst_in = reinterpret_cast<SceNetSockaddrIn *>(dst);
memset(dst_in, 0, sizeof(*dst_in));
dst_in->sin_len = sizeof(*dst_in);
sockaddr_in *src_in = reinterpret_cast<sockaddr_in *>(src);
dst_in->sin_family = static_cast<unsigned char>(src_in->sin_family);
dst_in->sin_port = src_in->sin_port;
memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4);
memcpy(&dst_in->sin_addr, &src_in->sin_addr, sizeof(dst_in->sin_addr));
}
static bool abort_pending(bool &is_aborded) {
if (is_aborded) {
is_aborded = false; // Reset the flag for the next operation
return true;
}
return false;
}
static bool should_abort(int abort_flags, int flags) {
return (abort_flags & flags) != 0;
}
int PosixSocket::connect(const SceNetSockaddr *addr, unsigned int namelen) {
sockaddr addr2;
if (should_abort(abort_flags, SCE_NET_SOCKET_ABORT_FLAG_SND_PRESERVATION))
return SCE_NET_ERROR_EINTR;
sockaddr addr2{};
convertSceSockaddrToPosix(addr, &addr2);
return translate_return_value(::connect(sock, &addr2, sizeof(sockaddr_in)));
const auto res = ::connect(sock, &addr2, sizeof(sockaddr_in));
if (abort_pending(is_aborted))
return SCE_NET_ERROR_EINTR;
return translate_return_value(res);
}
int PosixSocket::bind(const SceNetSockaddr *addr, unsigned int addrlen) {
@@ -172,6 +194,26 @@ int PosixSocket::get_socket_address(SceNetSockaddr *name, unsigned int *namelen)
return res;
}
int PosixSocket::abort(int flags) {
abort_flags |= flags;
is_aborted = true;
#ifdef _WIN32
auto res = CancelIoEx((HANDLE)sock, nullptr);
if (res == 0) {
DWORD err = WSAGetLastError();
if (err == ERROR_NOT_FOUND)
return SCE_NET_ERROR_ENOTBLK;
else
return SCE_NET_ERROR_EINTERNAL;
}
return 0;
#else
return translate_return_value(shutdown(sock, SHUT_RDWR));
#endif
}
int PosixSocket::close() {
#ifdef _WIN32
auto out = closesocket(sock);
@@ -181,9 +223,21 @@ int PosixSocket::close() {
return translate_return_value(out);
}
SocketPtr PosixSocket::accept(SceNetSockaddr *addr, unsigned int *addrlen) {
sockaddr addr2;
int PosixSocket::shutdown_socket(int how) {
return translate_return_value(shutdown(sock, how));
}
SocketPtr PosixSocket::accept(SceNetSockaddr *addr, unsigned int *addrlen, int &err) {
if (should_abort(abort_flags, SCE_NET_SOCKET_ABORT_FLAG_RCV_PRESERVATION)) {
err = SCE_NET_ERROR_EINTR;
return nullptr;
}
sockaddr addr2{};
abs_socket new_socket = ::accept(sock, &addr2, (socklen_t *)addrlen);
if (abort_pending(is_aborted)) {
err = SCE_NET_ERROR_EINTR;
return nullptr;
}
#ifdef _WIN32
if (new_socket != INVALID_SOCKET) {
#else
@@ -193,6 +247,7 @@ SocketPtr PosixSocket::accept(SceNetSockaddr *addr, unsigned int *addrlen) {
*addrlen = sizeof(SceNetSockaddrIn);
return std::make_shared<PosixSocket>(new_socket);
}
err = translate_return_value(new_socket);
return nullptr;
}
@@ -230,21 +285,46 @@ int PosixSocket::set_socket_options(int level, int optname, const void *optval,
CASE_SETSOCKOPT(SO_RCVBUF);
CASE_SETSOCKOPT(SO_SNDLOWAT);
CASE_SETSOCKOPT(SO_RCVLOWAT);
CASE_SETSOCKOPT(SO_SNDTIMEO);
CASE_SETSOCKOPT(SO_RCVTIMEO);
CASE_SETSOCKOPT(SO_ERROR);
CASE_SETSOCKOPT(SO_TYPE);
CASE_SETSOCKOPT_VALUE(SCE_NET_SO_REUSEPORT, &sockopt_so_reuseport);
CASE_SETSOCKOPT_VALUE(SCE_NET_SO_ONESBCAST, &sockopt_so_onesbcast);
CASE_SETSOCKOPT_VALUE(SCE_NET_SO_USECRYPTO, &sockopt_so_usecrypto);
CASE_SETSOCKOPT_VALUE(SCE_NET_SO_USESIGNATURE, &sockopt_so_usesignature);
CASE_SETSOCKOPT_VALUE(SCE_NET_SO_TPPOLICY, &sockopt_so_tppolicy);
case SCE_NET_SO_ONESBCAST:
if (optlen != sizeof(sockopt_so_onesbcast))
return SCE_NET_ERROR_EFAULT;
memcpy(&sockopt_so_onesbcast, optval, optlen);
// Sets the option to allow sending broadcast packets on a socket
return translate_return_value(setsockopt(sock, level, SO_BROADCAST, (const char *)optval, optlen));
case SCE_NET_SO_SNDTIMEO:
case SCE_NET_SO_RCVTIMEO: {
if (optlen != sizeof(int))
return SCE_NET_ERROR_EFAULT;
std::vector<char> val;
const auto optname_nat = (optname == SCE_NET_SO_SNDTIMEO) ? SO_SNDTIMEO : SO_RCVTIMEO;
int timeout_us = *(const int *)optval;
#ifdef _WIN32
DWORD timeout = timeout_us / 1000;
val.insert(val.end(), (char *)&timeout, (char *)&timeout + sizeof(timeout));
optlen = sizeof(timeout);
#else
timeval timeout{
.tv_sec = timeout_us / 1000000,
.tv_usec = timeout_us % 1000000
};
val.insert(val.end(), (char *)&timeout, (char *)&timeout + sizeof(timeout));
optlen = sizeof(timeout);
#endif
return translate_return_value(setsockopt(sock, level, optname_nat, val.data(), optlen));
}
case SCE_NET_SO_NAME:
return SCE_NET_ERROR_EINVAL; // don't support set for name
case SCE_NET_SO_NBIO: {
if (optlen != sizeof(sockopt_so_nbio)) {
if (optlen != sizeof(sockopt_so_nbio))
return SCE_NET_ERROR_EFAULT;
}
memcpy(&sockopt_so_nbio, optval, optlen);
#ifdef _WIN32
static_assert(sizeof(u_long) == sizeof(sockopt_so_nbio), "type used for ioctlsocket value does not have the expected size");
@@ -343,25 +423,89 @@ int PosixSocket::get_socket_options(int level, int optname, void *optval, unsign
return SCE_NET_ERROR_EINVAL;
}
int PosixSocket::recv_packet(void *buf, unsigned int len, int flags, SceNetSockaddr *from, unsigned int *fromlen) {
if (from != nullptr) {
sockaddr addr;
int res = recvfrom(sock, (char *)buf, len, flags, &addr, (socklen_t *)fromlen);
convertPosixSockaddrToSce(&addr, from);
*fromlen = sizeof(SceNetSockaddrIn);
static int convertSceFlagsToPosix(int sce_flags) {
int posix_flags = 0;
return translate_return_value(res);
} else {
return translate_return_value(recv(sock, (char *)buf, len, flags));
if (sce_flags & SCE_NET_MSG_PEEK)
posix_flags |= MSG_PEEK;
#ifndef _WIN32
if (sce_flags & SCE_NET_MSG_DONTWAIT)
posix_flags |= MSG_DONTWAIT;
#endif
if (sce_flags & SCE_NET_MSG_WAITALL)
posix_flags |= MSG_WAITALL;
return posix_flags;
}
// On Windows, MSG_DONTWAIT is not handled natively by recv/send.
// This function uses select() with zero timeout to simulate non-blocking behavior.
static int socket_is_ready(int sock, bool is_read = true) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
timeval timeout{ 0, 0 };
int res = select(sock + 1, is_read ? &fds : nullptr, is_read ? nullptr : &fds, nullptr, &timeout);
if (res == 0)
return SCE_NET_ERROR_EWOULDBLOCK;
else if (res < 0)
return PosixSocket::translate_return_value(res);
return res;
}
int PosixSocket::recv_packet(void *buf, unsigned int len, int flags, SceNetSockaddr *from, unsigned int *fromlen) {
if (should_abort(abort_flags, SCE_NET_SOCKET_ABORT_FLAG_RCV_PRESERVATION))
return SCE_NET_ERROR_EINTR;
int res = 0;
#ifdef _WIN32
if (flags & SCE_NET_MSG_DONTWAIT) {
res = socket_is_ready(sock);
if (res <= 0)
return res;
}
#endif
const auto posix_flags = convertSceFlagsToPosix(flags);
if (from == nullptr) {
res = recv(sock, (char *)buf, len, posix_flags);
} else {
sockaddr addr{};
socklen_t addrlen = sizeof(addr);
res = recvfrom(sock, (char *)buf, len, posix_flags, &addr, (fromlen && *fromlen <= sizeof(addr) ? (socklen_t *)fromlen : &addrlen));
if (res > 0)
convertPosixSockaddrToSce(&addr, from);
}
if (abort_pending(is_aborted))
return SCE_NET_ERROR_EINTR;
return translate_return_value(res);
}
int PosixSocket::send_packet(const void *msg, unsigned int len, int flags, const SceNetSockaddr *to, unsigned int tolen) {
if (to != nullptr) {
sockaddr addr;
convertSceSockaddrToPosix(to, &addr);
return translate_return_value(sendto(sock, (const char *)msg, len, flags, &addr, sizeof(sockaddr_in)));
} else {
return translate_return_value(send(sock, (const char *)msg, len, flags));
if (should_abort(abort_flags, SCE_NET_SOCKET_ABORT_FLAG_SND_PRESERVATION))
return SCE_NET_ERROR_EINTR;
int res = 0;
#ifdef _WIN32
if (flags & SCE_NET_MSG_DONTWAIT) {
res = socket_is_ready(sock, false);
if (res <= 0)
return res;
}
#endif
const auto posix_flags = convertSceFlagsToPosix(flags);
if (to == nullptr) {
res = send(sock, (const char *)msg, len, posix_flags);
} else {
sockaddr addr{};
convertSceSockaddrToPosix(to, &addr);
res = sendto(sock, (const char *)msg, len, posix_flags, &addr, tolen);
}
if (abort_pending(is_aborted))
return SCE_NET_ERROR_EINTR;
return translate_return_value(res);
}