mirror of
https://github.com/Vita3K/Vita3K.git
synced 2026-07-11 01:34:23 +02:00
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:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user