#include "ppsspp_config.h"
#include <algorithm>
#include <mutex>
#include <cstring>
#include "Common/Net/SocketCompat.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/System/OSD.h"
#include "Common/Thread/ThreadUtil.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/TimeUtil.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelMutex.h"
#include "Core/HLE/sceUtility.h"
#include "Core/MemMap.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLEHelperThread.h"
#include "Core/Config.h"
#include "Core/CoreTiming.h"
#include "Core/Core.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceNetAdhoc.h"
#include "Core/Instance.h"
#include "proAdhoc.h"
#include "Core/HLE/NetAdhocCommon.h"
#ifdef _WIN32
#undef errno
#define errno WSAGetLastError()
#endif
#if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE)
#define INADDR_NONE 0xFFFFFFFF
#endif
uint16_t portOffset;
uint32_t minSocketTimeoutUS;
uint32_t fakePoolSize = 0;
SceNetMallocStat netAdhocPoolStat = {};
SceNetAdhocMatchingContext * contexts = NULL;
char* dummyPeekBuf64k = NULL;
int dummyPeekBuf64kSize = 65536;
int one = 1;
std::atomic<bool> friendFinderRunning(false);
SceNetAdhocctlPeerInfo * friends = NULL;
SceNetAdhocctlScanInfo * networks = NULL;
SceNetAdhocctlScanInfo * newnetworks = NULL;
u64 adhocctlStartTime = 0;
bool isAdhocctlNeedLogin = false;
bool isAdhocctlBusy = false;
int adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
int adhocctlCurrentMode = ADHOCCTL_MODE_NONE;
int adhocConnectionType = ADHOC_CONNECT;
int gameModeSocket = (int)INVALID_SOCKET;
int gameModeBuffSize = 0;
u8* gameModeBuffer = nullptr;
GameModeArea masterGameModeArea;
std::vector<GameModeArea> replicaGameModeAreas;
std::vector<SceNetEtherAddr> requiredGameModeMacs;
std::vector<SceNetEtherAddr> gameModeMacs;
std::map<SceNetEtherAddr, u16_le> gameModePeerPorts;
int actionAfterAdhocMipsCall;
int actionAfterMatchingMipsCall;
uint8_t broadcastMAC[ETHER_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
std::atomic<int> metasocket((int)INVALID_SOCKET);
SceNetAdhocctlParameter parameter;
SceNetAdhocctlAdhocId product_code;
std::thread friendFinderThread;
std::recursive_mutex peerlock;
AdhocSocket* adhocSockets[MAX_SOCKET];
bool isOriPort = false;
bool isLocalServer = false;
SockAddrIN4 g_adhocServerIP;
SockAddrIN4 g_localhostIP;
sockaddr LocalIP;
int defaultWlanChannel = PSP_SYSTEMPARAM_ADHOC_CHANNEL_11;
static std::mutex chatLogLock;
static std::vector<std::string> chatLog;
static int chatMessageGeneration = 0;
static int chatMessageCount = 0;
bool isMacMatch(const SceNetEtherAddr* addr1, const SceNetEtherAddr* addr2) {
return (memcmp(((const char*)addr1)+1, ((const char*)addr2)+1, ETHER_ADDR_LEN-1) == 0);
}
bool isLocalMAC(const SceNetEtherAddr * addr) {
SceNetEtherAddr saddr;
getLocalMac(&saddr);
return isMacMatch(addr, &saddr);
}
bool isPDPPortInUse(uint16_t port) {
for (int i = 0; i < MAX_SOCKET; i++) {
auto sock = adhocSockets[i];
if (sock != NULL && sock->type == SOCK_PDP)
if (sock->data.pdp.lport == port)
return true;
}
return false;
}
bool isPTPPortInUse(uint16_t port, bool forListen, SceNetEtherAddr* dstmac, uint16_t dstport) {
for (int i = 0; i < MAX_SOCKET; i++) {
auto sock = adhocSockets[i];
if (sock != NULL && sock->type == SOCK_PTP)
if (sock->data.ptp.lport == port &&
((forListen && sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) ||
(!forListen && sock->data.ptp.state != ADHOC_PTP_STATE_LISTEN &&
sock->data.ptp.pport == dstport && dstmac != nullptr && isMacMatch(&sock->data.ptp.paddr, dstmac))))
{
return true;
}
}
return false;
}
std::string ip2str(in_addr in, bool maskPublicIP) {
char str[INET_ADDRSTRLEN] = "...";
u8* ipptr = (u8*)∈
#ifdef _DEBUG
maskPublicIP = false;
#endif
if (maskPublicIP && !isPrivateIP(in.s_addr))
snprintf(str, sizeof(str), "%u.%u.xx.%u", ipptr[0], ipptr[1], ipptr[3]);
else
snprintf(str, sizeof(str), "%u.%u.%u.%u", ipptr[0], ipptr[1], ipptr[2], ipptr[3]);
return std::string(str);
}
std::string mac2str(const SceNetEtherAddr *mac) {
char str[18] = ":::::";
if (mac != NULL) {
snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", mac->data[0], mac->data[1], mac->data[2], mac->data[3], mac->data[4], mac->data[5]);
}
return std::string(str);
}
SceNetAdhocMatchingMemberInternal* addMember(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac) {
if (context == NULL || mac == NULL) return NULL;
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
if (peer != NULL) {
WARN_LOG(Log::sceNet, "Member Peer Already Existed! Updating [%s]", mac2str(mac).c_str());
peer->state = 0;
peer->sending = 0;
peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
}
else {
peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
if (peer != NULL) {
memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal));
peer->mac = *mac;
peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
peerlock.lock();
peer->next = context->peerlist;
context->peerlist = peer;
peerlock.unlock();
}
}
return peer;
}
void addFriend(SceNetAdhocctlConnectPacketS2C * packet) {
if (packet == NULL) return;
std::lock_guard<std::recursive_mutex> guard(peerlock);
SceNetAdhocctlPeerInfo * peer = findFriend(&packet->mac);
if (peer != NULL) {
u32 tmpip = packet->ip;
WARN_LOG(Log::sceNet, "Friend Peer Already Existed! Updating [%s][%s][%s]", mac2str(&packet->mac).c_str(), ip2str(*(struct in_addr*)&tmpip).c_str(), packet->name.data);
peer->nickname = packet->name;
peer->mac_addr = packet->mac;
peer->ip_addr = packet->ip;
peer->port_offset = ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset);
peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
}
else {
peer = (SceNetAdhocctlPeerInfo *)malloc(sizeof(SceNetAdhocctlPeerInfo));
if (peer != NULL) {
memset(peer, 0, sizeof(SceNetAdhocctlPeerInfo));
peer->nickname = packet->name;
peer->mac_addr = packet->mac;
peer->ip_addr = packet->ip;
peer->port_offset = ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset);
peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
peer->next = friends;
friends = peer;
}
}
}
SceNetAdhocctlPeerInfo * findFriend(SceNetEtherAddr * MAC) {
if (MAC == NULL) return NULL;
SceNetAdhocctlPeerInfo * peer = friends;
for (; peer != NULL; peer = peer->next) {
if (isMacMatch(&peer->mac_addr, MAC)) break;
}
return peer;
}
SceNetAdhocctlPeerInfo* findFriendByIP(uint32_t ip) {
SceNetAdhocctlPeerInfo* peer = friends;
for (; peer != NULL; peer = peer->next) {
if (peer->ip_addr == ip) break;
}
return peer;
}
int IsSocketReady(int fd, bool readfd, bool writefd, int* errorcode, int timeoutUS) {
fd_set readfds, writefds;
timeval tval;
if (fd < 0) {
if (errorcode != nullptr)
*errorcode = EBADF;
return SOCKET_ERROR;
}
#if !defined(_WIN32)
if (fd >= FD_SETSIZE) {
if (errorcode != nullptr)
*errorcode = EBADF;
return SOCKET_ERROR;
}
#endif
FD_ZERO(&readfds);
writefds = readfds;
if (readfd) {
FD_SET(fd, &readfds);
}
if (writefd) {
FD_SET(fd, &writefds);
}
tval.tv_sec = timeoutUS / 1000000;
tval.tv_usec = timeoutUS % 1000000;
int ret = select(fd + 1, readfd? &readfds: nullptr, writefd? &writefds: nullptr, nullptr, &tval);
if (errorcode != nullptr)
*errorcode = (ret < 0 ? socket_errno : 0);
return ret;
}
void changeBlockingMode(int fd, int nonblocking) {
unsigned long on = 1;
unsigned long off = 0;
#if defined(_WIN32)
if (nonblocking) {
ioctlsocket(fd, FIONBIO, &on);
}
else {
ioctlsocket(fd, FIONBIO, &off);
}
#else
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
flags = 0;
if (nonblocking) {
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
else {
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
#endif
}
int countAvailableNetworks(const bool excludeSelf) {
int count = 0;
SceNetAdhocctlScanInfo * group = networks;
for (; group != NULL && (!excludeSelf || !isLocalMAC(&group->bssid.mac_addr)); group = group->next) count++;
return count;
}
SceNetAdhocctlScanInfo * findGroup(SceNetEtherAddr * MAC) {
if (MAC == NULL) return NULL;
SceNetAdhocctlScanInfo * group = networks;
for (; group != NULL; group = group->next) {
if (isMacMatch(&group->bssid.mac_addr, MAC)) break;
}
return group;
}
void freeGroupsRecursive(SceNetAdhocctlScanInfo * node) {
if (node == NULL) return;
freeGroupsRecursive(node->next);
free(node);
node = NULL;
}
void deleteAllAdhocSockets() {
for (int i = 0; i < MAX_SOCKET; i++) {
if (adhocSockets[i] != NULL) {
auto sock = adhocSockets[i];
int fd = -1;
if (sock->type == SOCK_PTP)
fd = sock->data.ptp.id;
else if (sock->type == SOCK_PDP)
fd = sock->data.pdp.id;
if (fd > 0) {
shutdown(fd, SD_RECEIVE);
closesocket(fd);
}
free(adhocSockets[i]);
adhocSockets[i] = NULL;
}
}
}
void deleteAllGMB() {
if (gameModeBuffer) {
free(gameModeBuffer);
gameModeBuffer = nullptr;
gameModeBuffSize = 0;
}
if (masterGameModeArea.data) {
free(masterGameModeArea.data);
masterGameModeArea = { 0 };
}
for (auto& it : replicaGameModeAreas) {
if (it.data) {
free(it.data);
it.data = nullptr;
}
}
replicaGameModeAreas.clear();
gameModeMacs.clear();
requiredGameModeMacs.clear();
}
void deleteFriendByIP(uint32_t ip) {
SceNetAdhocctlPeerInfo * prev = NULL;
SceNetAdhocctlPeerInfo * peer = friends;
for (; peer != NULL; peer = peer->next) {
if (peer->ip_addr == ip) {
peerlock.lock();
u32 tmpip = peer->ip_addr;
INFO_LOG(Log::sceNet, "Removing Friend Peer %s [%s]", mac2str(&peer->mac_addr).c_str(), ip2str(*(struct in_addr *)&tmpip).c_str());
peer->last_recv = 0;
peerlock.unlock();
break;
}
prev = peer;
}
}
int findFreeMatchingID() {
int min = 1;
int max = 0;
SceNetAdhocMatchingContext * item = contexts;
for (; item != NULL; item = item->next) {
if (max < item->id) max = item->id;
}
int i = min;
for (; i < max; i++) {
if (findMatchingContext(i) == NULL) return i;
}
return max + 1;
}
SceNetAdhocMatchingContext * findMatchingContext(int id) {
SceNetAdhocMatchingContext * item = contexts;
for (; item != NULL; item = item->next) {
if (item->id == id) return item;
}
return NULL;
}
SceNetAdhocMatchingMemberInternal * findOutgoingRequest(SceNetAdhocMatchingContext * context)
{
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
for (; peer != NULL; peer = peer->next)
{
if (peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST) return peer;
}
return NULL;
}
void postAcceptCleanPeerList(SceNetAdhocMatchingContext * context)
{
int delcount = 0;
int peercount = 0;
peerlock.lock();
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
while (peer != NULL)
{
SceNetAdhocMatchingMemberInternal * next = peer->next;
if (peer->state != PSP_ADHOC_MATCHING_PEER_CHILD && peer->state != PSP_ADHOC_MATCHING_PEER_P2P && peer->state != PSP_ADHOC_MATCHING_PEER_PARENT && peer->state != 0) {
deletePeer(context, peer);
delcount++;
}
peer = next;
peercount++;
}
peerlock.unlock();
INFO_LOG(Log::sceNet, "Removing Unneeded Peers (%i/%i)", delcount, peercount);
}
void postAcceptAddSiblings(SceNetAdhocMatchingContext * context, int siblingcount, SceNetEtherAddr * siblings)
{
uint8_t * siblings_u8 = (uint8_t *)siblings;
peerlock.lock();
for (int i = siblingcount - 1; i >= 0 ; i--)
{
SceNetEtherAddr* mac = (SceNetEtherAddr*)(siblings_u8 + sizeof(SceNetEtherAddr) * i);
auto peer = findPeer(context, mac);
if (peer != NULL) {
peer->state = PSP_ADHOC_MATCHING_PEER_CHILD;
peer->sending = 0;
peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
WARN_LOG(Log::sceNet, "Updating Sibling Peer %s", mac2str(mac).c_str());
}
else {
SceNetAdhocMatchingMemberInternal* sibling = (SceNetAdhocMatchingMemberInternal*)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
if (sibling != NULL)
{
memset(sibling, 0, sizeof(SceNetAdhocMatchingMemberInternal));
memcpy(&sibling->mac, mac, sizeof(SceNetEtherAddr));
sibling->state = PSP_ADHOC_MATCHING_PEER_CHILD;
sibling->lastping = CoreTiming::GetGlobalTimeUsScaled();
sibling->next = context->peerlist;
context->peerlist = sibling;
INFO_LOG(Log::sceNet, "Accepting Sibling Peer %s", mac2str(&sibling->mac).c_str());
}
}
}
peerlock.unlock();
}
s32_le countChildren(SceNetAdhocMatchingContext * context, const bool excludeTimedout)
{
s32_le count = 0;
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
for (; peer != NULL; peer = peer->next)
{
if (!excludeTimedout || peer->lastping != 0)
if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) count++;
}
return count;
}
SceNetAdhocMatchingMemberInternal * findPeer(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac)
{
if (mac == NULL)
return NULL;
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
for (; peer != NULL; peer = peer->next)
{
if (isMacMatch(&peer->mac, mac))
{
return peer;
}
}
return NULL;
}
SceNetAdhocMatchingMemberInternal * findParent(SceNetAdhocMatchingContext * context)
{
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
for (; peer != NULL; peer = peer->next)
{
if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) return peer;
}
return NULL;
}
SceNetAdhocMatchingMemberInternal * findP2P(SceNetAdhocMatchingContext * context, const bool excludeTimedout)
{
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
for (; peer != NULL; peer = peer->next)
{
if (!excludeTimedout || peer->lastping != 0)
if (peer->state == PSP_ADHOC_MATCHING_PEER_P2P) return peer;
}
return NULL;
}
void deletePeer(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal *& peer)
{
if (context != NULL && peer != NULL)
{
peerlock.lock();
SceNetAdhocMatchingMemberInternal * previous = NULL;
SceNetAdhocMatchingMemberInternal * item = context->peerlist;
for (; item != NULL; item = item->next)
{
if (item == peer) break;
previous = item;
}
if (item != NULL) {
if (previous != NULL) previous->next = item->next;
else context->peerlist = item->next;
INFO_LOG(Log::sceNet, "Removing Member Peer %s", mac2str(&peer->mac).c_str());
}
free(peer);
peer = NULL;
peerlock.unlock();
}
}
void linkEVMessage(SceNetAdhocMatchingContext * context, ThreadMessage * message)
{
context->eventlock->lock();
message->next = context->event_stack;
context->event_stack = message;
context->eventlock->unlock();
}
void linkIOMessage(SceNetAdhocMatchingContext * context, ThreadMessage * message)
{
context->inputlock->lock();
message->next = context->input_stack;
context->input_stack = message;
context->inputlock->unlock();
}
void sendGenericMessage(SceNetAdhocMatchingContext * context, int stack, SceNetEtherAddr * mac, int opcode, int optlen, const void * opt)
{
uint32_t size = sizeof(ThreadMessage) + optlen;
uint8_t * memory = (uint8_t *)malloc(size);
if (memory != NULL)
{
memset(memory, 0, size);
ThreadMessage * header = (ThreadMessage *)memory;
header->opcode = opcode;
header->mac = *mac;
header->optlen = optlen;
memcpy(memory + sizeof(ThreadMessage), opt, optlen);
if (stack == PSP_ADHOC_MATCHING_EVENT_STACK) linkEVMessage(context, header);
else linkIOMessage(context, header);
return;
}
peerlock.lock();
auto peer = findPeer(context, mac);
deletePeer(context, peer);
peerlock.unlock();
}
void sendAcceptMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_ACCEPT, optlen, opt);
}
void sendJoinRequest(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_JOIN, optlen, opt);
}
void sendCancelMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_CANCEL, optlen, opt);
}
void sendBulkData(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int datalen, const void * data)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BULK, datalen, data);
}
void abortBulkTransfer(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BULK_ABORT, 0, NULL);
}
void sendBirthMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BIRTH, 0, NULL);
}
void sendDeathMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_DEATH, 0, NULL);
}
uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context, const bool excludeTimedout)
{
uint32_t count = 0;
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
{
count = countChildren(context, excludeTimedout) + 1;
}
else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
{
count = 1;
if (findParent(context) != NULL)
{
count += countChildren(context, excludeTimedout) + 1;
}
}
else
{
count = 1;
if (findP2P(context, excludeTimedout) != NULL)
{
count++;
}
}
return count;
}
void spawnLocalEvent(SceNetAdhocMatchingContext * context, int event, SceNetEtherAddr * mac, int optlen, void * opt)
{
sendGenericMessage(context, PSP_ADHOC_MATCHING_EVENT_STACK, mac, event, optlen, opt);
}
void handleTimeout(SceNetAdhocMatchingContext * context)
{
peerlock.lock();
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
while (peer != NULL && contexts != NULL && coreState != CORE_POWERDOWN)
{
SceNetAdhocMatchingMemberInternal * next = peer->next;
u64_le now = CoreTiming::GetGlobalTimeUsScaled();
if (peer->state != 0 && static_cast<s64>(now - peer->lastping) > static_cast<s64>(context->timeout))
{
if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_P2P &&
(peer->state == PSP_ADHOC_MATCHING_PEER_P2P || peer->state == PSP_ADHOC_MATCHING_PEER_OFFER || peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST || peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST || peer->state == PSP_ADHOC_MATCHING_PEER_CANCEL_IN_PROGRESS)))
{
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_TIMEOUT, &peer->mac, 0, NULL);
INFO_LOG(Log::sceNet, "TimedOut Member Peer %s (%lld - %lld = %lld > %lld us)", mac2str(&peer->mac).c_str(), now, peer->lastping, (now - peer->lastping), context->timeout);
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
sendDeathMessage(context, peer);
else
sendCancelMessage(context, peer, 0, NULL);
}
}
peer = next;
}
peerlock.unlock();
}
void clearStackRecursive(ThreadMessage *& node)
{
if (node != NULL) clearStackRecursive(node->next);
free(node);
node = NULL;
}
void clearStack(SceNetAdhocMatchingContext * context, int stack)
{
if (context == NULL) return;
if (stack == PSP_ADHOC_MATCHING_EVENT_STACK)
{
context->eventlock->lock();
clearStackRecursive(context->event_stack);
context->event_stack = NULL;
context->eventlock->unlock();
}
else
{
context->inputlock->lock();
clearStackRecursive(context->input_stack);
context->input_stack = NULL;
context->inputlock->unlock();
}
}
void clearPeerList(SceNetAdhocMatchingContext * context)
{
peerlock.lock();
SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
while (peer != NULL)
{
context->peerlist = peer->next;
free(peer);
peer = context->peerlist;
}
peerlock.unlock();
}
void AfterMatchingMipsCall::DoState(PointerWrap & p) {
auto s = p.Section("AfterMatchingMipsCall", 1, 4);
if (!s)
return;
if (s >= 1) {
Do(p, EventID);
} else {
EventID = -1;
}
if (s >= 4) {
Do(p, contextID);
Do(p, bufAddr);
} else {
contextID = -1;
bufAddr = 0;
}
}
void AfterMatchingMipsCall::run(MipsCall &call) {
if (context == NULL) {
peerlock.lock();
context = findMatchingContext(contextID);
peerlock.unlock();
}
u32 v0 = currentMIPS->r[MIPS_REG_V0];
if (__IsInInterrupt()) ERROR_LOG(Log::sceNet, "AfterMatchingMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", contextID, EventID);
DEBUG_LOG(Log::sceNet, "AfterMatchingMipsCall::run [ID=%i][Event=%d][%s] [cbId: %u][retV0: %08x]", contextID, EventID, mac2str((SceNetEtherAddr*)Memory::GetPointer(bufAddr)).c_str(), call.cbId, v0);
if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr);
}
void AfterMatchingMipsCall::SetData(int ContextID, int eventId, u32_le BufAddr) {
contextID = ContextID;
EventID = eventId;
bufAddr = BufAddr;
peerlock.lock();
context = findMatchingContext(ContextID);
peerlock.unlock();
}
bool SetMatchingInCallback(SceNetAdhocMatchingContext* context, bool IsInCB) {
if (context == NULL) return false;
peerlock.lock();
context->IsMatchingInCB = IsInCB;
peerlock.unlock();
return IsInCB;
}
bool IsMatchingInCallback(SceNetAdhocMatchingContext* context) {
bool inCB = false;
if (context == NULL) return inCB;
peerlock.lock();
inCB = (context->IsMatchingInCB);
peerlock.unlock();
return inCB;
}
void AfterAdhocMipsCall::DoState(PointerWrap & p) {
auto s = p.Section("AfterAdhocMipsCall", 1, 4);
if (!s)
return;
if (s >= 3) {
Do(p, HandlerID);
Do(p, EventID);
Do(p, argsAddr);
} else {
HandlerID = -1;
EventID = -1;
argsAddr = 0;
}
}
void AfterAdhocMipsCall::run(MipsCall& call) {
u32 v0 = currentMIPS->r[MIPS_REG_V0];
if (__IsInInterrupt()) ERROR_LOG(Log::sceNet, "AfterAdhocMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", HandlerID, EventID);
SetAdhocctlInCallback(false);
isAdhocctlBusy = false;
DEBUG_LOG(Log::sceNet, "AfterAdhocMipsCall::run [ID=%i][Event=%d] [cbId: %u][retV0: %08x]", HandlerID, EventID, call.cbId, v0);
}
void AfterAdhocMipsCall::SetData(int handlerID, int eventId, u32_le ArgsAddr) {
HandlerID = handlerID;
EventID = eventId;
argsAddr = ArgsAddr;
}
int SetAdhocctlInCallback(bool IsInCB) {
std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
IsAdhocctlInCB += (IsInCB?1:-1);
return IsAdhocctlInCB;
}
int IsAdhocctlInCallback() {
std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
int inCB = IsAdhocctlInCB;
return inCB;
}
void notifyAdhocctlHandlers(u32 flag, u32 error) {
__UpdateAdhocctlHandlers(flag, error);
}
void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) {
if (node == NULL) return;
freeFriendsRecursive(node->next, count);
free(node);
node = NULL;
if (count != NULL) (*count)++;
}
void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) {
if (node == NULL) return;
timeoutFriendsRecursive(node->next, count);
node->last_recv = 0;
if (count != NULL) (*count)++;
}
void sendChat(const std::string &chatString) {
SceNetAdhocctlChatPacketC2S chat{};
chat.base.opcode = OPCODE_CHAT;
if (friendFinderRunning) {
if (!chatString.empty()) {
std::string message = chatString.substr(0, 60);
strcpy(chat.message, message.c_str());
if (IsSocketReady((int)metasocket, false, true) > 0) {
int chatResult = (int)send((int)metasocket, (const char*)&chat, sizeof(chat), MSG_NOSIGNAL);
NOTICE_LOG(Log::sceNet, "Send Chat %s to Adhoc Server", chat.message);
std::string name = g_Config.sNickName;
std::lock_guard<std::mutex> guard(chatLogLock);
chatLog.emplace_back(name.substr(0, 8) + ": " + chat.message);
chatMessageGeneration++;
}
}
} else {
std::lock_guard<std::mutex> guard(chatLogLock);
auto n = GetI18NCategory(I18NCat::NETWORKING);
chatLog.push_back(std::string(n->T("You're in Offline Mode, go to lobby or online hall")));
chatMessageGeneration++;
}
}
std::vector<std::string> getChatLog() {
std::lock_guard<std::mutex> guard(chatLogLock);
if (chatLog.size() > 50) {
chatLog.erase(chatLog.begin(), chatLog.begin() + (chatLog.size() - 50));
}
return chatLog;
}
int GetChatChangeID() {
return chatMessageGeneration;
}
int GetChatMessageCount() {
return chatMessageCount;
}
int friendFinder() {
SetCurrentThreadName("FriendFinder");
auto n = GetI18NCategory(I18NCat::NETWORKING);
int rxpos = 0;
uint8_t rx[1024];
SceNetAdhocctlChatPacketC2S chat;
chat.base.opcode = OPCODE_CHAT;
uint64_t lastping = 0;
uint64_t lastreceptionupdate = 0;
uint64_t now;
INFO_LOG(Log::sceNet, "FriendFinder: Begin of Friend Finder Thread");
addrinfo* resolved = nullptr;
std::string err;
g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE;
if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.proAdhocServer, "", &resolved, err)) {
ERROR_LOG(Log::sceNet, "DNS Error Resolving %s\n", g_Config.proAdhocServer.c_str());
g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("DNS Error Resolving ")) + g_Config.proAdhocServer);
}
if (resolved) {
for (auto ptr = resolved; ptr != NULL; ptr = ptr->ai_next) {
switch (ptr->ai_family) {
case AF_INET:
g_adhocServerIP.in = *(sockaddr_in*)ptr->ai_addr;
break;
}
}
net::DNSResolveFree(resolved);
}
g_adhocServerIP.in.sin_port = htons(SERVER_PORT);
friendFinderRunning = true;
while (friendFinderRunning) {
if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited && isAdhocctlNeedLogin) {
if (g_Config.bEnableWlan) {
if (initNetwork(&product_code) == 0) {
g_adhocServerConnected = true;
INFO_LOG(Log::sceNet, "FriendFinder: Network [RE]Initialized");
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
netAdhocGameModeEntered = false;
isAdhocctlBusy = false;
}
else {
g_adhocServerConnected = false;
shutdown((int)metasocket, SD_BOTH);
closesocket((int)metasocket);
metasocket = (int)INVALID_SOCKET;
}
}
}
isAdhocctlNeedLogin = false;
if (g_adhocServerConnected) {
now = time_now_d() * 1000000.0;
if (static_cast<s64>(now - lastping) >= PSP_ADHOCCTL_PING_TIMEOUT) {
uint8_t opcode = OPCODE_PING;
if (IsSocketReady((int)metasocket, false, true) > 0) {
int iResult = (int)send((int)metasocket, (const char*)&opcode, 1, MSG_NOSIGNAL);
int error = socket_errno;
if (iResult == SOCKET_ERROR) {
ERROR_LOG(Log::sceNet, "FriendFinder: Socket Error (%i) when sending OPCODE_PING", error);
if (error != EAGAIN && error != EWOULDBLOCK) {
g_adhocServerConnected = false;
shutdown((int)metasocket, SD_BOTH);
closesocket((int)metasocket);
metasocket = (int)INVALID_SOCKET;
g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Disconnected from AdhocServer")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(error) + ")");
peerlock.lock();
timeoutFriendsRecursive(friends);
peerlock.unlock();
}
}
else {
lastping = now;
VERBOSE_LOG(Log::sceNet, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast<unsigned long long>(now));
}
}
}
if (IsSocketReady((int)metasocket, true, false) > 0) {
int received = (int)recv((int)metasocket, (char*)(rx + rxpos), sizeof(rx) - rxpos, MSG_NOSIGNAL);
if (received > 0) {
rxpos += received;
INFO_LOG(Log::sceNet, "Received %d Bytes of Data from Adhoc Server", received);
}
}
if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast<s64>(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) {
netAdhocGameModeEntered = false;
notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOC_TIMEOUT);
}
if (rxpos > 0) {
if (rx[0] == OPCODE_CONNECT_BSSID) {
if (rxpos >= (int)sizeof(SceNetAdhocctlConnectBSSIDPacketS2C)) {
SceNetAdhocctlConnectBSSIDPacketS2C* packet = (SceNetAdhocctlConnectBSSIDPacketS2C*)rx;
INFO_LOG(Log::sceNet, "FriendFinder: Incoming OPCODE_CONNECT_BSSID [%s]", mac2str(&packet->mac).c_str());
parameter.bssid.mac_addr = packet->mac;
if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
SceNetEtherAddr localMac;
getLocalMac(&localMac);
if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(),
[localMac](SceNetEtherAddr const& e) {
return isMacMatch(&e, &localMac);
}) == gameModeMacs.end()) {
gameModeMacs.push_back(localMac);
if (netAdhocGameModeEntered && gameModeMacs.size() >= requiredGameModeMacs.size()) {
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
}
}
else
WARN_LOG(Log::sceNet, "GameMode SelfMember [%s] Already Existed!", mac2str(&localMac).c_str());
}
else {
notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0);
}
memmove(rx, rx + sizeof(SceNetAdhocctlConnectBSSIDPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectBSSIDPacketS2C));
rxpos -= sizeof(SceNetAdhocctlConnectBSSIDPacketS2C);
}
}
else if (rx[0] == OPCODE_CHAT) {
if (rxpos >= (int)sizeof(SceNetAdhocctlChatPacketS2C)) {
SceNetAdhocctlChatPacketS2C* packet = (SceNetAdhocctlChatPacketS2C*)rx;
INFO_LOG(Log::sceNet, "FriendFinder: Incoming OPCODE_CHAT");
packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
packet->base.message[ADHOCCTL_MESSAGE_LEN - 1] = 0;
NOTICE_LOG(Log::sceNet, "Received chat message %s", packet->base.message);
std::string incoming = "";
std::string name = (char*)packet->name.data;
incoming.append(name.substr(0, 8));
incoming.append(": ");
incoming.append((char*)packet->base.message);
std::lock_guard<std::mutex> guard(chatLogLock);
chatLog.push_back(incoming);
chatMessageGeneration++;
chatMessageCount++;
memmove(rx, rx + sizeof(SceNetAdhocctlChatPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlChatPacketS2C));
rxpos -= sizeof(SceNetAdhocctlChatPacketS2C);
}
}
else if (rx[0] == OPCODE_CONNECT) {
if (rxpos >= (int)sizeof(SceNetAdhocctlConnectPacketS2C)) {
SceNetAdhocctlConnectPacketS2C* packet = (SceNetAdhocctlConnectPacketS2C*)rx;
packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
u32_le ipaddr = packet->ip;
INFO_LOG(Log::sceNet, "FriendFinder: Incoming OPCODE_CONNECT [%s][%s][%s]", mac2str(&packet->mac).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), packet->name.data);
addFriend(packet);
if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(),
[packet](SceNetEtherAddr const& e) {
return isMacMatch(&e, &packet->mac);
}) == gameModeMacs.end()) {
SceNetEtherAddr localMac;
getLocalMac(&localMac);
auto it = std::find_if(gameModeMacs.begin(), gameModeMacs.end(),
[localMac](SceNetEtherAddr const& e) {
return isMacMatch(&e, &localMac);
});
if (it != gameModeMacs.end()) {
gameModeMacs.push_back(packet->mac);
}
else {
it = gameModeMacs.begin() + 1;
gameModeMacs.insert(it, packet->mac);
}
if (netAdhocGameModeEntered && requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) {
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
}
}
else
WARN_LOG(Log::sceNet, "GameMode Member [%s] Already Existed!", mac2str(&packet->mac).c_str());
}
std::string name = (char*)packet->name.data;
std::string incoming = "";
incoming.append(name.substr(0, 8));
incoming.append(" Joined ");
std::lock_guard<std::mutex> guard(chatLogLock);
chatLog.push_back(incoming);
chatMessageGeneration++;
#ifdef LOCALHOST_AS_PEER
setUserCount(getActivePeerCount());
#else
#endif
memmove(rx, rx + sizeof(SceNetAdhocctlConnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectPacketS2C));
rxpos -= sizeof(SceNetAdhocctlConnectPacketS2C);
}
}
else if (rx[0] == OPCODE_DISCONNECT) {
if (rxpos >= (int)sizeof(SceNetAdhocctlDisconnectPacketS2C)) {
SceNetAdhocctlDisconnectPacketS2C* packet = (SceNetAdhocctlDisconnectPacketS2C*)rx;
DEBUG_LOG(Log::sceNet, "FriendFinder: OPCODE_DISCONNECT");
INFO_LOG(Log::sceNet, "FriendFinder: Incoming Peer Data Delete Request...");
if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
auto peer = findFriendByIP(packet->ip);
for (auto& gma : replicaGameModeAreas)
if (isMacMatch(&gma.mac, &peer->mac_addr)) {
gma.updateTimestamp = 0;
break;
}
}
deleteFriendByIP(packet->ip);
#ifdef LOCALHOST_AS_PEER
setUserCount(_getActivePeerCount());
#else
#endif
memmove(rx, rx + sizeof(SceNetAdhocctlDisconnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlDisconnectPacketS2C));
rxpos -= sizeof(SceNetAdhocctlDisconnectPacketS2C);
}
}
else if (rx[0] == OPCODE_SCAN) {
if (rxpos >= (int)sizeof(SceNetAdhocctlScanPacketS2C)) {
SceNetAdhocctlScanPacketS2C* packet = (SceNetAdhocctlScanPacketS2C*)rx;
DEBUG_LOG(Log::sceNet, "FriendFinder: OPCODE_SCAN");
INFO_LOG(Log::sceNet, "Incoming Group Information...");
peerlock.lock();
SceNetAdhocctlScanInfo* group = (SceNetAdhocctlScanInfo*)malloc(sizeof(SceNetAdhocctlScanInfo));
if (group != NULL) {
memset(group, 0, sizeof(SceNetAdhocctlScanInfo));
group->next = newnetworks;
group->group_name = packet->group;
group->bssid.mac_addr = packet->mac;
group->channel = parameter.channel;
group->mode = std::max(ADHOCCTL_MODE_NORMAL, adhocctlCurrentMode);
newnetworks = group;
}
peerlock.unlock();
memmove(rx, rx + sizeof(SceNetAdhocctlScanPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlScanPacketS2C));
rxpos -= sizeof(SceNetAdhocctlScanPacketS2C);
}
}
else if (rx[0] == OPCODE_SCAN_COMPLETE) {
DEBUG_LOG(Log::sceNet, "FriendFinder: OPCODE_SCAN_COMPLETE");
INFO_LOG(Log::sceNet, "FriendFinder: Incoming Scan complete response...");
peerlock.lock();
if (networks != newnetworks) {
freeGroupsRecursive(networks);
networks = newnetworks;
}
newnetworks = NULL;
peerlock.unlock();
notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0);
memmove(rx, rx + 1, sizeof(rx) - 1);
rxpos -= 1;
}
}
}
sleep_ms(10, "pro-adhoc-poll-2");
while (Core_IsStepping() && coreState != CORE_POWERDOWN && friendFinderRunning)
sleep_ms(10, "pro-adhoc-paused-poll-2");
}
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
friendFinderRunning = false;
INFO_LOG(Log::sceNet, "FriendFinder: End of Friend Finder Thread");
return 0;
}
int getActivePeerCount(const bool excludeTimedout) {
int count = 0;
SceNetAdhocctlPeerInfo * peer = friends;
for (; peer != NULL; peer = peer->next) {
if (!excludeTimedout || peer->last_recv != 0)
count++;
}
return count;
}
int getLocalIp(sockaddr_in* SocketAddress) {
if (isLocalServer) {
SocketAddress->sin_addr = g_localhostIP.in.sin_addr;
return 0;
}
#if !PPSSPP_PLATFORM(SWITCH)
if (metasocket != (int)INVALID_SOCKET) {
struct sockaddr_in localAddr {};
localAddr.sin_addr.s_addr = INADDR_ANY;
socklen_t addrLen = sizeof(localAddr);
int ret = getsockname((int)metasocket, (struct sockaddr*)&localAddr, &addrLen);
if (SOCKET_ERROR != ret && localAddr.sin_addr.s_addr != 0) {
SocketAddress->sin_addr = localAddr.sin_addr;
return 0;
}
}
#endif
#if (defined(_IFADDRS_H_) || (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) || (__ANDROID_API__ >= 24))
struct ifaddrs* ifAddrStruct = NULL;
struct ifaddrs* ifa = NULL;
getifaddrs(&ifAddrStruct);
if (ifAddrStruct != NULL) {
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) {
SocketAddress->sin_addr = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr;
u32 addr = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr;
if (addr != 0x0100007f) {
break;
}
}
}
freeifaddrs(ifAddrStruct);
return 0;
}
#else
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != SOCKET_ERROR) {
const char* kGoogleDnsIp = "8.8.8.8";
uint16_t kDnsPort = 53;
struct sockaddr_in serv {};
u32 ipv4 = INADDR_NONE;
inet_pton(AF_INET, kGoogleDnsIp, &ipv4);
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = ipv4;
serv.sin_port = htons(kDnsPort);
int err = connect(sock, (struct sockaddr*)&serv, sizeof(serv));
if (err != SOCKET_ERROR) {
struct sockaddr_in name {};
socklen_t namelen = sizeof(name);
err = getsockname(sock, (struct sockaddr*)&name, &namelen);
if (err != SOCKET_ERROR) {
SocketAddress->sin_addr = name.sin_addr;
closesocket(sock);
return 0;
}
}
closesocket(sock);
}
#endif
return -1;
}
uint32_t getLocalIp(int sock) {
struct sockaddr_in localAddr {};
localAddr.sin_addr.s_addr = INADDR_ANY;
socklen_t addrLen = sizeof(localAddr);
getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);
if (isLocalServer) {
localAddr.sin_addr = g_localhostIP.in.sin_addr;
}
return localAddr.sin_addr.s_addr;
}
static std::vector<std::pair<uint32_t, uint32_t>> InitPrivateIPRanges() {
struct sockaddr_in saNet {}, saMask{};
std::vector<std::pair<uint32_t, uint32_t>> ip_ranges;
if (1 == inet_pton(AF_INET, "192.168.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({saNet.sin_addr.s_addr, saMask.sin_addr.s_addr});
if (1 == inet_pton(AF_INET, "172.16.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.240.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
if (1 == inet_pton(AF_INET, "10.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
if (1 == inet_pton(AF_INET, "127.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
if (1 == inet_pton(AF_INET, "169.254.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
return ip_ranges;
}
bool isPrivateIP(uint32_t ip) {
static const std::vector<std::pair<uint32_t, uint32_t>> ip_ranges = InitPrivateIPRanges();
for (auto& ipRange : ip_ranges) {
if ((ip & ipRange.second) == (ipRange.first & ipRange.second))
return true;
}
return false;
}
bool isAPIPA(uint32_t ip) {
return (((uint8_t*)&ip)[0] == 169 && ((uint8_t*)&ip)[1] == 254);
}
bool isLoopbackIP(uint32_t ip) {
return ((uint8_t*)&ip)[0] == 0x7f;
}
bool isMulticastIP(uint32_t ip) {
return ((ip & 0xF0) == 0xE0);
}
bool isBroadcastIP(uint32_t ip, const uint32_t subnetmask) {
return (ip == (ip | (~subnetmask)));
}
void getLocalMac(SceNetEtherAddr * addr){
uint8_t mac[ETHER_ADDR_LEN] = {0};
if (PPSSPP_ID > 1) {
memset(&mac, PPSSPP_ID, sizeof(mac));
mac[0] &= 0xfc;
}
else
if (!ParseMacAddress(g_Config.sMACAddress, mac)) {
ERROR_LOG(Log::sceNet, "Error parsing mac address %s", g_Config.sMACAddress.c_str());
memset(&mac, 0, sizeof(mac));
}
memcpy(addr, mac, ETHER_ADDR_LEN);
}
uint16_t getLocalPort(int sock) {
struct sockaddr_in localAddr {};
localAddr.sin_port = 0;
socklen_t addrLen = sizeof(localAddr);
getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);
return ntohs(localAddr.sin_port);
}
u_long getAvailToRecv(int sock, int udpBufferSize) {
u_long n = 0;
int err = -1;
#if defined(_WIN32)
err = ioctlsocket(sock, FIONREAD, &n);
#else
err = ioctl(sock, FIONREAD, &n);
#endif
if (err < 0)
return 0;
if (udpBufferSize > 0 && n > 0) {
}
return n;
}
int getSockMaxSize(int udpsock) {
int n = PSP_ADHOC_PDP_MTU;
#if defined(SO_MAX_MSG_SIZE)
socklen_t m = sizeof(n);
getsockopt(udpsock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*)&n, &m);
#endif
return n;
}
int getSockBufferSize(int sock, int opt) {
int n = PSP_ADHOC_PDP_MFS*2;
socklen_t m = sizeof(n);
getsockopt(sock, SOL_SOCKET, opt, (char *)&n, &m);
return (n);
}
int setSockBufferSize(int sock, int opt, int size) {
int n = size;
switch (opt) {
case SO_RCVBUF: n = std::max(size, 128); break;
case SO_SNDBUF: n = std::max(size, 1024); break;
}
return setsockopt(sock, SOL_SOCKET, opt, (char *)&n, sizeof(n));
}
int setSockMSS(int sock, int size) {
int mss = size;
return setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char*)&mss, sizeof(mss));
}
int setSockTimeout(int sock, int opt, unsigned long timeout_usec) {
if (timeout_usec > 0 && timeout_usec < minSocketTimeoutUS) timeout_usec = minSocketTimeoutUS;
#if defined(_WIN32)
unsigned long optval = timeout_usec / 1000UL;
if (timeout_usec > 0 && optval == 0) optval = 1;
#elif defined(__APPLE__)
struct timeval optval;
optval.tv_sec = static_cast<long>(timeout_usec) / 1000000L;
optval.tv_usec = static_cast<long>(timeout_usec) % 1000000L;
#else
struct timeval optval = { static_cast<long>(timeout_usec) / 1000000L, static_cast<long>(timeout_usec) % 1000000L };
#endif
return setsockopt(sock, SOL_SOCKET, opt, (char*)&optval, sizeof(optval));
}
int getSockError(int sock) {
int result = 0;
socklen_t result_len = sizeof(result);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&result, &result_len) < 0) {
result = socket_errno;
}
return result;
}
int getSockNoDelay(int tcpsock) {
int opt = 0;
socklen_t optlen = sizeof(opt);
getsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, &optlen);
return opt;
}
int setSockNoDelay(int tcpsock, int flag) {
int opt = flag;
#if defined(TCP_QUICKACK)
setsockopt(tcpsock, IPPROTO_TCP, TCP_QUICKACK, (char*)&opt, sizeof(opt));
#elif defined(_WIN32)
#if !defined(SIO_TCP_SET_ACK_FREQUENCY)
#define SIO_TCP_SET_ACK_FREQUENCY _WSAIOW(IOC_VENDOR,23)
#endif
int freq = flag? 1:2;
DWORD retbytes = 0;
WSAIoctl(tcpsock, SIO_TCP_SET_ACK_FREQUENCY, &freq, sizeof(freq), NULL, 0, &retbytes, NULL, NULL);
#endif
return setsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));
}
int setSockNoSIGPIPE(int sock, int flag) {
int opt = flag;
#if defined(SO_NOSIGPIPE)
return setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt));
#endif
return -1;
}
int setSockReuseAddrPort(int sock) {
int opt = 1;
#if defined(SO_REUSEPORT)
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&opt, sizeof(opt));
#endif
return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
}
int setUDPConnReset(int udpsock, bool enabled) {
#if defined(_WIN32)
#if !defined(SIO_UDP_CONNRESET)
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
#endif
BOOL bNewBehavior = enabled;
DWORD dwBytesReturned = 0;
return WSAIoctl(udpsock, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
#endif
return -1;
}
#if !defined(TCP_KEEPIDLE) && !PPSSPP_PLATFORM(SWITCH)
#define TCP_KEEPIDLE TCP_KEEPALIVE
#endif
#if _MSC_VER
#ifndef TCP_KEEPCNT
#define TCP_KEEPCNT 16
#endif
#ifndef TCP_KEEPINTVL
#define TCP_KEEPINTVL 17
#endif
#endif
int setSockKeepAlive(int sock, bool keepalive, const int keepinvl, const int keepcnt, const int keepidle) {
int optval = keepalive ? 1 : 0;
int optlen = sizeof(optval);
int result = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, optlen);
#if !PPSSPP_PLATFORM(SWITCH) && !PPSSPP_PLATFORM(OPENBSD)
if (result == 0 && keepalive) {
if (getsockopt(sock, SOL_SOCKET, SO_TYPE, (char*)&optval, (socklen_t*)&optlen) == 0 && optval == SOCK_STREAM) {
optlen = sizeof(optval);
optval = keepidle;
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&optval, optlen);
optval = keepinvl;
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&optval, optlen);
optval = keepcnt;
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&optval, optlen);
}
}
#endif
return result;
}
int getNicknameCount(const char * nickname)
{
int count = 0;
if (strncmp((char *)¶meter.nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++;
SceNetAdhocctlPeerInfo * peer = friends;
for (; peer != NULL; peer = peer->next)
{
if (peer->last_recv != 0 && strncmp((char *)&peer->nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++;
}
return count;
}
int getPDPSocketCount()
{
int counter = 0;
for (int i = 0; i < MAX_SOCKET; i++)
if (adhocSockets[i] != NULL && adhocSockets[i]->type == SOCK_PDP)
counter++;
return counter;
}
int getPTPSocketCount() {
int counter = 0;
for (int i = 0; i < MAX_SOCKET; i++)
if (adhocSockets[i] != NULL && adhocSockets[i]->type == SOCK_PTP)
counter++;
return counter;
}
int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
auto n = GetI18NCategory(I18NCat::NETWORKING);
int iResult = 0;
metasocket = (int)INVALID_SOCKET;
metasocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (metasocket == INVALID_SOCKET){
ERROR_LOG(Log::sceNet, "Invalid socket");
return SOCKET_ERROR;
}
setSockKeepAlive((int)metasocket, true);
setSockNoDelay((int)metasocket, 1);
changeBlockingMode((int)metasocket, 1);
setSockNoSIGPIPE((int)metasocket, 1);
if (isLoopbackIP(g_adhocServerIP.in.sin_addr.s_addr)) {
int on = 1;
setsockopt((int)metasocket, SOL_SOCKET, SO_DONTROUTE, (const char*)&on, sizeof(on));
setSockReuseAddrPort((int)metasocket);
g_localhostIP.in.sin_port = 0;
iResult = bind((int)metasocket, &g_localhostIP.addr, sizeof(g_localhostIP.addr));
if (iResult == SOCKET_ERROR) {
ERROR_LOG(Log::sceNet, "Bind to alternate localhost[%s] failed(%i).", ip2str(g_localhostIP.in.sin_addr).c_str(), iResult);
g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to Bind Localhost IP")) + " " + ip2str(g_localhostIP.in.sin_addr).c_str());
}
}
memset(¶meter, 0, sizeof(parameter));
strncpy((char *)¶meter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);
parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
parameter.channel = g_Config.iWlanAdhocChannel;
if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel;
product_code.type = adhoc_id->type;
memcpy(product_code.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);
if (g_adhocServerIP.in.sin_addr.s_addr == INADDR_NONE)
return SOCKET_ERROR;
if (g_adhocServerIP.in.sin_addr.s_addr == g_localhostIP.in.sin_addr.s_addr && !g_Config.bEnableAdhocServer)
return SOCKET_ERROR;
int errorcode = 0;
int cnt = 0;
DEBUG_LOG(Log::sceNet, "InitNetwork: Connecting to AdhocServer");
iResult = connect((int)metasocket, &g_adhocServerIP.addr, sizeof(g_adhocServerIP));
errorcode = socket_errno;
if (iResult == SOCKET_ERROR && errorcode != EISCONN) {
u64 startTime = (u64)(time_now_d() * 1000000.0);
bool done = false;
while (!done) {
if (coreState == CORE_POWERDOWN)
return iResult;
done = (IsSocketReady((int)metasocket, false, true) > 0);
struct sockaddr_in sin;
socklen_t sinlen = sizeof(sin);
memset(&sin, 0, sinlen);
done &= (getpeername((int)metasocket, (struct sockaddr*)&sin, &sinlen) != SOCKET_ERROR);
u64 now = (u64)(time_now_d() * 1000000.0);
if (static_cast<s64>(now - startTime) > adhocDefaultTimeout) {
if (connectInProgress(errorcode))
errorcode = ETIMEDOUT;
break;
}
sleep_ms(10, "pro-adhoc-socket-poll");
}
if (!done) {
ERROR_LOG(Log::sceNet, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port));
g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")");
return iResult;
}
}
SceNetAdhocctlLoginPacketC2S packet;
packet.base.opcode = OPCODE_LOGIN;
SceNetEtherAddr addres;
getLocalMac(&addres);
packet.mac = addres;
strncpy((char *)&packet.name.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);
packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);
IsSocketReady((int)metasocket, false, true, nullptr, adhocDefaultTimeout);
DEBUG_LOG(Log::sceNet, "InitNetwork: Sending LOGIN OPCODE %d", packet.base.opcode);
int sent = (int)send((int)metasocket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL);
if (sent > 0) {
socklen_t addrLen = sizeof(LocalIP);
memset(&LocalIP, 0, addrLen);
getsockname((int)metasocket, &LocalIP, &addrLen);
return 0;
} else {
return SOCKET_ERROR;
}
}
bool isZeroMAC(const SceNetEtherAddr* addr) {
return (memcmp(addr->data, "\x00\x00\x00\x00\x00\x00", ETHER_ADDR_LEN) == 0);
}
bool isBroadcastMAC(const SceNetEtherAddr * addr) {
return (memcmp(addr->data, "\xFF\xFF\xFF\xFF\xFF\xFF", ETHER_ADDR_LEN) == 0);
}
bool resolveIP(uint32_t ip, SceNetEtherAddr * mac) {
sockaddr_in addr;
getLocalIp(&addr);
uint32_t localIp = addr.sin_addr.s_addr;
if (ip == localIp || ip == g_localhostIP.in.sin_addr.s_addr) {
getLocalMac(mac);
return true;
}
std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
SceNetAdhocctlPeerInfo * peer = friends;
for (; peer != NULL; peer = peer->next) {
if (peer->ip_addr == ip) {
*mac = peer->mac_addr;
return true;
}
}
return false;
}
bool resolveMAC(SceNetEtherAddr* mac, uint32_t* ip, u16* port_offset) {
SceNetEtherAddr localMac;
getLocalMac(&localMac);
if (isMacMatch(&localMac, mac)) {
sockaddr_in sockAddr;
getLocalIp(&sockAddr);
*ip = sockAddr.sin_addr.s_addr;
if (port_offset)
*port_offset = portOffset;
return true;
}
std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
SceNetAdhocctlPeerInfo * peer = friends;
for (; peer != NULL; peer = peer->next) {
if (isMacMatch(&peer->mac_addr, mac)) {
*ip = peer->ip_addr;
if (port_offset)
*port_offset = peer->port_offset;
return true;
}
}
return false;
}
bool validNetworkName(const char *data) {
bool valid = true;
if (data != NULL) {
for (int i = 0; i < ADHOCCTL_GROUPNAME_LEN && valid; i++) {
if (data[i] == 0) break;
if (data[i] < '0' || data[i] > '9') {
if (data[i] < 'A' || data[i] > 'Z') {
if (data[i] < 'a' || data[i] > 'z') {
valid = false;
}
}
}
}
}
return valid;
}
u64 join32(u32 num1, u32 num2){
return (u64)num2 << 32 | num1;
}
void split64(u64 num, int buff[]){
int num1 = (int)(num&firstMask);
int num2 = (int)((num&secondMask) >> 32);
buff[0] = num1;
buff[1] = num2;
}
const char* getMatchingEventStr(int code) {
const char *buf = NULL;
switch (code) {
case PSP_ADHOC_MATCHING_EVENT_HELLO:
buf = "HELLO"; break;
case PSP_ADHOC_MATCHING_EVENT_REQUEST:
buf = "JOIN"; break;
case PSP_ADHOC_MATCHING_EVENT_LEAVE:
buf = "LEAVE"; break;
case PSP_ADHOC_MATCHING_EVENT_DENY:
buf = "REJECT"; break;
case PSP_ADHOC_MATCHING_EVENT_CANCEL:
buf = "CANCEL"; break;
case PSP_ADHOC_MATCHING_EVENT_ACCEPT:
buf = "ACCEPT"; break;
case PSP_ADHOC_MATCHING_EVENT_ESTABLISHED:
buf = "ESTABLISHED"; break;
case PSP_ADHOC_MATCHING_EVENT_TIMEOUT:
buf = "TIMEOUT"; break;
case PSP_ADHOC_MATCHING_EVENT_ERROR:
buf = "ERROR"; break;
case PSP_ADHOC_MATCHING_EVENT_BYE:
buf = "DISCONNECT"; break;
case PSP_ADHOC_MATCHING_EVENT_DATA:
buf = "DATA"; break;
case PSP_ADHOC_MATCHING_EVENT_DATA_ACK:
buf = "DATA_ACK"; break;
case PSP_ADHOC_MATCHING_EVENT_DATA_TIMEOUT:
buf = "DATA_TIMEOUT"; break;
case PSP_ADHOC_MATCHING_EVENT_INTERNAL_PING:
buf = "INTERNAL_PING"; break;
default:
buf = "UNKNOWN";
}
return buf;
}
const char* getMatchingOpcodeStr(int code) {
const char *buf = NULL;
switch (code) {
case PSP_ADHOC_MATCHING_PACKET_PING:
buf = "PING"; break;
case PSP_ADHOC_MATCHING_PACKET_HELLO:
buf = "HELLO"; break;
case PSP_ADHOC_MATCHING_PACKET_JOIN:
buf = "JOIN"; break;
case PSP_ADHOC_MATCHING_PACKET_ACCEPT:
buf = "ACCEPT"; break;
case PSP_ADHOC_MATCHING_PACKET_CANCEL:
buf = "CANCEL"; break;
case PSP_ADHOC_MATCHING_PACKET_BULK:
buf = "BULK"; break;
case PSP_ADHOC_MATCHING_PACKET_BULK_ABORT:
buf = "BULK_ABORT"; break;
case PSP_ADHOC_MATCHING_PACKET_BIRTH:
buf = "BIRTH"; break;
case PSP_ADHOC_MATCHING_PACKET_DEATH:
buf = "DEATH"; break;
case PSP_ADHOC_MATCHING_PACKET_BYE:
buf = "BYE"; break;
default:
buf = "UNKNOWN";
}
return buf;
}
const char *AdhocCtlStateToString(int state) {
switch (state) {
case ADHOCCTL_STATE_DISCONNECTED: return "DISCONNECTED";
case ADHOCCTL_STATE_CONNECTED: return "CONNECTED";
case ADHOCCTL_STATE_SCANNING: return "SCANNING";
case ADHOCCTL_STATE_GAMEMODE: return "GAMEMODE";
case ADHOCCTL_STATE_DISCOVER: return "DISCOVER";
case ADHOCCTL_STATE_WOL: return "WOL";
default: return "(unk)";
}
}