mirror of https://github.com/PCSX2/pcsx2.git
DEV9: Add Socket api mode
This commit is contained in:
parent
140d44d826
commit
352faeddb2
|
@ -32,12 +32,14 @@
|
|||
#ifdef _WIN32
|
||||
#include "DEV9/Win32/tap.h"
|
||||
#endif
|
||||
#include "DEV9/sockets.h"
|
||||
|
||||
static const char* s_api_name[] = {
|
||||
QT_TRANSLATE_NOOP("DEV9SettingsWidget", " "),
|
||||
QT_TRANSLATE_NOOP("DEV9SettingsWidget", "PCAP Bridged"),
|
||||
QT_TRANSLATE_NOOP("DEV9SettingsWidget", "PCAP Switched"),
|
||||
QT_TRANSLATE_NOOP("DEV9SettingsWidget", "TAP"),
|
||||
QT_TRANSLATE_NOOP("DEV9SettingsWidget", "Sockets"),
|
||||
nullptr,
|
||||
};
|
||||
|
||||
|
@ -145,6 +147,8 @@ DEV9SettingsWidget::DEV9SettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
|||
for (const AdapterEntry& adapter : TAPAdapter::GetAdapters())
|
||||
AddAdapter(adapter);
|
||||
#endif
|
||||
for (const AdapterEntry& adapter : SocketAdapter::GetAdapters())
|
||||
AddAdapter(adapter);
|
||||
|
||||
std::sort(m_api_list.begin(), m_api_list.end());
|
||||
for (auto& list : m_adapter_list)
|
||||
|
@ -400,6 +404,9 @@ void DEV9SettingsWidget::onEthDeviceTypeChanged(int index)
|
|||
case Pcsx2Config::DEV9Options::NetApi::PCAP_Switched:
|
||||
m_adapter_options = PCAPAdapter::GetAdapterOptions();
|
||||
break;
|
||||
case Pcsx2Config::DEV9Options::NetApi::Sockets:
|
||||
m_adapter_options = SocketAdapter::GetAdapterOptions();
|
||||
break;
|
||||
default:
|
||||
m_adapter_options = AdapterOptions::None;
|
||||
break;
|
||||
|
|
|
@ -369,7 +369,15 @@ set(pcsx2DEV9Sources
|
|||
DEV9/PacketReader/IP/IP_Packet.cpp
|
||||
DEV9/PacketReader/EthernetFrame.cpp
|
||||
DEV9/PacketReader/NetLib.cpp
|
||||
DEV9/Sessions/BaseSession.cpp
|
||||
DEV9/Sessions/ICMP_Session/ICMP_Session.cpp
|
||||
DEV9/Sessions/TCP_Session/TCP_Session.cpp
|
||||
DEV9/Sessions/TCP_Session/TCP_Session_In.cpp
|
||||
DEV9/Sessions/TCP_Session/TCP_Session_Out.cpp
|
||||
DEV9/Sessions/UDP_Session/UDP_FixedPort.cpp
|
||||
DEV9/Sessions/UDP_Session/UDP_Session.cpp
|
||||
DEV9/smap.cpp
|
||||
DEV9/sockets.cpp
|
||||
DEV9/DEV9.cpp
|
||||
DEV9/flash.cpp
|
||||
DEV9/pcap_io.cpp
|
||||
|
@ -403,8 +411,16 @@ set(pcsx2DEV9Headers
|
|||
DEV9/PacketReader/NetLib.h
|
||||
DEV9/PacketReader/Payload.h
|
||||
DEV9/pcap_io.h
|
||||
DEV9/Sessions/BaseSession.h
|
||||
DEV9/Sessions/ICMP_Session/ICMP_Session.h
|
||||
DEV9/Sessions/TCP_Session/TCP_Session.h
|
||||
DEV9/Sessions/UDP_Session/UDP_FixedPort.h
|
||||
DEV9/Sessions/UDP_Session/UDP_BaseSession.h
|
||||
DEV9/Sessions/UDP_Session/UDP_Session.h
|
||||
DEV9/SimpleQueue.h
|
||||
DEV9/smap.h
|
||||
DEV9/sockets.h
|
||||
DEV9/ThreadSafeMap.h
|
||||
)
|
||||
|
||||
if(NOT PCSX2_CORE)
|
||||
|
|
|
@ -651,6 +651,7 @@ struct Pcsx2Config
|
|||
PCAP_Bridged = 1,
|
||||
PCAP_Switched = 2,
|
||||
TAP = 3,
|
||||
Sockets = 4,
|
||||
};
|
||||
static const char* NetApiNames[];
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "Config.h"
|
||||
#include "DEV9.h"
|
||||
#include "pcap_io.h"
|
||||
#include "sockets.h"
|
||||
#include "net.h"
|
||||
#include "PacketReader/IP/IP_Address.h"
|
||||
#include "gui/AppCoreThread.h"
|
||||
|
@ -138,6 +139,9 @@ public:
|
|||
for (const AdapterEntry& adapter : TAPAdapter::GetAdapters())
|
||||
addAdapter(adapter);
|
||||
#endif
|
||||
for (const AdapterEntry& adapter : SocketAdapter::GetAdapters())
|
||||
addAdapter(adapter);
|
||||
|
||||
std::sort(m_api_list.begin(), m_api_list.end());
|
||||
for (auto& list : m_adapter_list)
|
||||
std::sort(list.begin(), list.end(), [](const AdapterEntry& a, AdapterEntry& b){ return a.name < b.name; });
|
||||
|
@ -309,6 +313,9 @@ public:
|
|||
case Pcsx2Config::DEV9Options::NetApi::PCAP_Switched:
|
||||
adapterOptions = PCAPAdapter::GetAdapterOptions();
|
||||
break;
|
||||
case Pcsx2Config::DEV9Options::NetApi::Sockets:
|
||||
adapterOptions = SocketAdapter::GetAdapterOptions();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -121,4 +121,22 @@ namespace PacketReader::IP::ICMP
|
|||
|
||||
return (csumCal == 0);
|
||||
}
|
||||
|
||||
ICMP_HeaderDataIdentifier::ICMP_HeaderDataIdentifier(u16 id, u16 seq)
|
||||
: identifier{id}
|
||||
, sequenceNumber{seq}
|
||||
{
|
||||
}
|
||||
ICMP_HeaderDataIdentifier::ICMP_HeaderDataIdentifier(u8* headerData)
|
||||
{
|
||||
int offset = 0;
|
||||
NetLib::ReadUInt16(headerData, &offset, &identifier);
|
||||
NetLib::ReadUInt16(headerData, &offset, &sequenceNumber);
|
||||
}
|
||||
void ICMP_HeaderDataIdentifier::WriteHeaderData(u8* headerData)
|
||||
{
|
||||
int offset = 0;
|
||||
NetLib::WriteUInt16(headerData, &offset, identifier);
|
||||
NetLib::WriteUInt16(headerData, &offset, sequenceNumber);
|
||||
}
|
||||
} // namespace PacketReader::IP::ICMP
|
||||
|
|
|
@ -53,4 +53,17 @@ namespace PacketReader::IP::ICMP
|
|||
virtual bool VerifyChecksum(IP_Address srcIP, IP_Address dstIP);
|
||||
virtual void CalculateChecksum(IP_Address srcIP, IP_Address dstIP);
|
||||
};
|
||||
|
||||
//Helper Classes
|
||||
//Do we want this? or do we do the same as with options?
|
||||
class ICMP_HeaderDataIdentifier
|
||||
{
|
||||
public:
|
||||
u16 identifier;
|
||||
u16 sequenceNumber;
|
||||
|
||||
ICMP_HeaderDataIdentifier(u16 id, u16 seq);
|
||||
ICMP_HeaderDataIdentifier(u8* headerData);
|
||||
void WriteHeaderData(u8* headerData);
|
||||
};
|
||||
} // namespace PacketReader::IP::ICMP
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "BaseSession.h"
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
bool ConnectionKey::operator==(const ConnectionKey& other) const
|
||||
{
|
||||
return (*(u32*)&ip == *(u32*)&other.ip) &&
|
||||
(protocol == other.protocol) &&
|
||||
(ps2Port == other.ps2Port) &&
|
||||
(srvPort == other.srvPort);
|
||||
}
|
||||
bool ConnectionKey::operator!=(const ConnectionKey& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
BaseSession::BaseSession(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP)
|
||||
: key{parKey}
|
||||
, adapterIP{parAdapterIP}
|
||||
{
|
||||
}
|
||||
|
||||
void BaseSession::AddConnectionClosedHandler(ConnectionClosedEventHandler handler)
|
||||
{
|
||||
connectionClosedHandlers.push_back(handler);
|
||||
}
|
||||
|
||||
void BaseSession::RaiseEventConnectionClosed()
|
||||
{
|
||||
std::vector<ConnectionClosedEventHandler> Handlers = connectionClosedHandlers;
|
||||
connectionClosedHandlers.clear();
|
||||
|
||||
for (size_t i = 0; i < Handlers.size(); i++)
|
||||
Handlers[i](this);
|
||||
}
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,84 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DEV9/PacketReader/IP/IP_Packet.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
class BaseSession; //Forward declare
|
||||
|
||||
typedef std::function<void(BaseSession*)> ConnectionClosedEventHandler;
|
||||
|
||||
struct ConnectionKey
|
||||
{
|
||||
PacketReader::IP::IP_Address ip{0};
|
||||
u8 protocol = 0;
|
||||
u16 ps2Port = 0;
|
||||
u16 srvPort = 0;
|
||||
|
||||
bool operator==(const ConnectionKey& other) const;
|
||||
bool operator!=(const ConnectionKey& other) const;
|
||||
};
|
||||
|
||||
class BaseSession
|
||||
{
|
||||
public:
|
||||
ConnectionKey key;
|
||||
PacketReader::IP::IP_Address sourceIP;
|
||||
PacketReader::IP::IP_Address destIP;
|
||||
|
||||
protected:
|
||||
PacketReader::IP::IP_Address adapterIP;
|
||||
|
||||
private:
|
||||
std::vector<ConnectionClosedEventHandler> connectionClosedHandlers;
|
||||
|
||||
public:
|
||||
BaseSession(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP);
|
||||
|
||||
void AddConnectionClosedHandler(ConnectionClosedEventHandler handler);
|
||||
|
||||
virtual PacketReader::IP::IP_Payload* Recv() = 0;
|
||||
virtual bool Send(PacketReader::IP::IP_Payload* payload) = 0;
|
||||
virtual void Reset() = 0;
|
||||
|
||||
virtual ~BaseSession() {}
|
||||
|
||||
protected:
|
||||
void RaiseEventConnectionClosed();
|
||||
};
|
||||
} // namespace Sessions
|
||||
|
||||
//ConnectionKey Hash function
|
||||
template <>
|
||||
struct std::hash<Sessions::ConnectionKey>
|
||||
{
|
||||
size_t operator()(Sessions::ConnectionKey const& s) const noexcept
|
||||
{
|
||||
size_t hash = 17;
|
||||
hash = hash * 23 + std::hash<u8>{}(s.ip.bytes[0]);
|
||||
hash = hash * 23 + std::hash<u8>{}(s.ip.bytes[1]);
|
||||
hash = hash * 23 + std::hash<u8>{}(s.ip.bytes[2]);
|
||||
hash = hash * 23 + std::hash<u8>{}(s.ip.bytes[3]);
|
||||
hash = hash * 23 + std::hash<u8>{}(s.protocol);
|
||||
hash = hash * 23 + std::hash<u16>{}(s.ps2Port);
|
||||
hash = hash * 23 + std::hash<u16>{}(s.srvPort);
|
||||
return hash;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,901 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <iphlpapi.h>
|
||||
#include <icmpapi.h>
|
||||
#elif defined(__POSIX__)
|
||||
|
||||
#if defined(__linux__)
|
||||
#define ICMP_SOCKETS_LINUX
|
||||
#endif
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
#define ICMP_SOCKETS_BSD
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/errqueue.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "ICMP_Session.h"
|
||||
#include "DEV9/PacketReader/NetLib.h"
|
||||
|
||||
using namespace PacketReader;
|
||||
using namespace PacketReader::IP;
|
||||
using namespace PacketReader::IP::ICMP;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/* Ping is kindof annoying to do crossplatform
|
||||
All platforms restrict raw sockets
|
||||
|
||||
Windows provides an api for ICMP
|
||||
ICMP_ECHO_REPLY should always be used, ignore ICMP_ECHO_REPLY32
|
||||
IP_OPTION_INFORMATION should always be used, ignore IP_OPTION_INFORMATION32
|
||||
|
||||
Linux
|
||||
We have access to raw sockets via CAP_NET_RAW (for pcap)
|
||||
However we may be missing that cap on some builds
|
||||
Linux has socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP), used similar to raw sockets but for ICMP only
|
||||
Auto filters responses
|
||||
Requires net.ipv4.ping_group_range sysctl, default off on alot of distros
|
||||
Timeouts reported via sock_extended_err control messages (with IP_RECVERR socket option set)
|
||||
|
||||
Mac
|
||||
Raw sockets restricted
|
||||
Mac has socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)
|
||||
No restriction to using it
|
||||
Implementation differs, is more versatile than linux
|
||||
Does not auto filter responses
|
||||
Timeouts reported as a normal packet
|
||||
|
||||
FreeBSD
|
||||
Raw sockets restricted
|
||||
No unprivilaged ICMP sockets
|
||||
Timeouts reported as a normal packet??
|
||||
|
||||
Ping cli
|
||||
Present for all platforms, but command args differ
|
||||
*/
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
const std::chrono::duration<std::chrono::steady_clock::rep, std::chrono::steady_clock::period>
|
||||
ICMP_Session::Ping::ICMP_TIMEOUT = 30s;
|
||||
|
||||
#ifdef __POSIX__
|
||||
ICMP_Session::Ping::PingType ICMP_Session::Ping::icmpConnectionKind = ICMP_Session::Ping::PingType::ICMP;
|
||||
#endif
|
||||
|
||||
ICMP_Session::Ping::Ping(int requestSize)
|
||||
#ifdef _WIN32
|
||||
: icmpFile{IcmpCreateFile()}
|
||||
{
|
||||
if (icmpFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Failed to Create Icmp File");
|
||||
return;
|
||||
}
|
||||
|
||||
icmpEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (icmpEvent == NULL)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Failed to Create Event");
|
||||
IcmpCloseHandle(icmpFile);
|
||||
icmpFile = INVALID_HANDLE_VALUE;
|
||||
return;
|
||||
}
|
||||
|
||||
//Allocate return buffer
|
||||
//Documentation says + 8 to allow for an ICMP error message
|
||||
//In testing, ICMP_ECHO_REPLY structure itself was returned with data set to null
|
||||
icmpResponseBufferLen = sizeof(ICMP_ECHO_REPLY) + requestSize + 8;
|
||||
icmpResponseBuffer = std::make_unique<u8[]>(icmpResponseBufferLen);
|
||||
#elif defined(__POSIX__)
|
||||
{
|
||||
switch (icmpConnectionKind)
|
||||
{
|
||||
//Two different methods for raw/icmp sockets bettween the unix OSes
|
||||
//Play it safe and only enable when we know which of the two methods we use
|
||||
#if defined(ICMP_SOCKETS_LINUX) || defined(ICMP_SOCKETS_BSD)
|
||||
case (PingType::ICMP):
|
||||
icmpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
if (icmpSocket != -1)
|
||||
{
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
//Space for ICMP header, as MSG_ERRQUEUE returns what we sent
|
||||
//An extra +8 required sometimes, for some reason?
|
||||
icmpResponseBufferLen = 8 + requestSize + 8;
|
||||
#elif defined(ICMP_SOCKETS_BSD)
|
||||
//Returned IP Header, ICMP Header & either data or failed ICMP packet
|
||||
icmpResponseBufferLen = 20 + 8 + std::max(20 + 8, requestSize);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
DevCon.WriteLn("DEV9: ICMP: Failed To Open ICMP Socket");
|
||||
icmpConnectionKind = ICMP_Session::Ping::PingType::RAW;
|
||||
|
||||
[[fallthrough]];
|
||||
|
||||
case (PingType::RAW):
|
||||
icmpSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (icmpSocket != -1)
|
||||
{
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
//We get packet + header
|
||||
icmpResponseBufferLen = 20 + 8 + requestSize;
|
||||
#elif defined(ICMP_SOCKETS_BSD)
|
||||
//As above, but we will also directly recive error ICMP messages
|
||||
icmpResponseBufferLen = 20 + 8 + std::max(20 + 8, requestSize);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
DevCon.WriteLn("DEV9: ICMP: Failed To Open RAW Socket");
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
default:
|
||||
Console.Error("DEV9: ICMP: Failed To Ping");
|
||||
return;
|
||||
}
|
||||
|
||||
icmpResponseBuffer = std::make_unique<u8[]>(icmpResponseBufferLen);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ICMP_Session::Ping::IsInitialised()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return icmpFile != INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
switch (icmpConnectionKind)
|
||||
{
|
||||
case (PingType::ICMP):
|
||||
case (PingType::RAW):
|
||||
return icmpSocket != -1;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//Note, we can finish reading but have no data
|
||||
ICMP_Session::PingResult* ICMP_Session::Ping::Recv()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (WaitForSingleObject(icmpEvent, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
ResetEvent(icmpEvent);
|
||||
//Prep buffer for reasing
|
||||
int count = IcmpParseReplies(icmpResponseBuffer.get(), icmpResponseBufferLen);
|
||||
pxAssert(count == 1);
|
||||
ICMP_ECHO_REPLY* pingRet = (ICMP_ECHO_REPLY*)icmpResponseBuffer.get();
|
||||
|
||||
//Map status to ICMP type/code
|
||||
switch (pingRet->Status)
|
||||
{
|
||||
case (IP_SUCCESS):
|
||||
result.type = 0;
|
||||
result.code = 0;
|
||||
break;
|
||||
case (IP_DEST_NET_UNREACHABLE):
|
||||
result.type = 3;
|
||||
result.code = 0;
|
||||
break;
|
||||
case (IP_DEST_HOST_UNREACHABLE):
|
||||
result.type = 3;
|
||||
result.code = 1;
|
||||
break;
|
||||
case (IP_DEST_PROT_UNREACHABLE):
|
||||
result.type = 3;
|
||||
result.code = 2;
|
||||
break;
|
||||
case (IP_DEST_PORT_UNREACHABLE):
|
||||
result.type = 3;
|
||||
result.code = 3;
|
||||
break;
|
||||
case (IP_PACKET_TOO_BIG):
|
||||
result.type = 3;
|
||||
result.code = 4;
|
||||
break;
|
||||
case (IP_BAD_ROUTE): //Bad source route
|
||||
result.type = 3;
|
||||
result.code = 5;
|
||||
break;
|
||||
case (IP_BAD_DESTINATION):
|
||||
//I think this could be either
|
||||
//Destination network unknown
|
||||
//or
|
||||
//Destination host unknown
|
||||
//Use host unkown
|
||||
result.type = 3;
|
||||
result.code = 7;
|
||||
break;
|
||||
case (IP_REQ_TIMED_OUT):
|
||||
//Return nothing
|
||||
result.type = -2;
|
||||
result.code = 0;
|
||||
break;
|
||||
case (IP_TTL_EXPIRED_TRANSIT):
|
||||
result.type = 11;
|
||||
result.code = 0;
|
||||
break;
|
||||
case (IP_TTL_EXPIRED_REASSEM):
|
||||
result.type = 11;
|
||||
result.code = 1;
|
||||
break;
|
||||
case (IP_SOURCE_QUENCH):
|
||||
result.type = 4;
|
||||
result.code = 0;
|
||||
break;
|
||||
|
||||
//Unexpected Errors
|
||||
case (IP_BUF_TOO_SMALL):
|
||||
case (IP_NO_RESOURCES):
|
||||
case (IP_BAD_OPTION):
|
||||
case (IP_HW_ERROR):
|
||||
case (IP_BAD_REQ):
|
||||
case (IP_PARAM_PROBLEM):
|
||||
case (IP_OPTION_TOO_BIG):
|
||||
case (IP_GENERAL_FAILURE):
|
||||
default:
|
||||
result.type = -1;
|
||||
result.code = pingRet->Status;
|
||||
break;
|
||||
}
|
||||
|
||||
result.dataLength = pingRet->DataSize;
|
||||
result.data = (u8*)pingRet->Data;
|
||||
result.address.integer = pingRet->Address;
|
||||
|
||||
return &result;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
#elif defined(__POSIX__)
|
||||
switch (icmpConnectionKind)
|
||||
{
|
||||
case (PingType::ICMP):
|
||||
case (PingType::RAW):
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined(ICMP_SOCKETS_BSD)
|
||||
fd_set sReady;
|
||||
fd_set sExcept;
|
||||
|
||||
timeval nowait{0};
|
||||
FD_ZERO(&sReady);
|
||||
FD_ZERO(&sExcept);
|
||||
FD_SET(icmpSocket, &sReady);
|
||||
FD_SET(icmpSocket, &sExcept);
|
||||
ret = select(icmpSocket + 1, &sReady, nullptr, &sExcept, &nowait);
|
||||
|
||||
bool hasData;
|
||||
if (ret == -1)
|
||||
{
|
||||
hasData = false;
|
||||
Console.WriteLn("DEV9: ICMP: select failed. Error Code: %d", errno);
|
||||
}
|
||||
else if (FD_ISSET(icmpSocket, &sExcept))
|
||||
{
|
||||
hasData = false;
|
||||
|
||||
int error = 0;
|
||||
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(icmpSocket, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
|
||||
Console.Error("DEV9: ICMP: Unkown ICMP Connection Error (getsockopt Error: %d)", errno);
|
||||
else
|
||||
Console.Error("DEV9: ICMP: Recv Error: %d", error);
|
||||
}
|
||||
else
|
||||
hasData = FD_ISSET(icmpSocket, &sReady);
|
||||
|
||||
if (hasData == false)
|
||||
{
|
||||
if (std::chrono::steady_clock::now() - icmpDeathClockStart > ICMP_TIMEOUT)
|
||||
{
|
||||
result.type = -2;
|
||||
result.code = 0;
|
||||
return &result;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
sockaddr endpoint{0};
|
||||
|
||||
iovec iov;
|
||||
iov.iov_base = icmpResponseBuffer.get();
|
||||
iov.iov_len = icmpResponseBufferLen;
|
||||
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
//Needs to hold cmsghdr + sock_extended_err + sockaddr_in
|
||||
//for ICMP error responses (total 44 bytes)
|
||||
//Unkown for other types of error
|
||||
u8 cbuff[64];
|
||||
#endif
|
||||
|
||||
msghdr msg{0};
|
||||
msg.msg_name = &endpoint;
|
||||
msg.msg_namelen = sizeof(endpoint);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
ret = recvmsg(icmpSocket, &msg, MSG_DONTWAIT);
|
||||
#elif defined(ICMP_SOCKETS_BSD)
|
||||
ret = recvmsg(icmpSocket, &msg, 0);
|
||||
#endif
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
int err = errno;
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
if (err == EAGAIN || err == EWOULDBLOCK)
|
||||
{
|
||||
if (std::chrono::steady_clock::now() - icmpDeathClockStart > ICMP_TIMEOUT)
|
||||
{
|
||||
result.type = -2;
|
||||
result.code = 0;
|
||||
return &result;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.msg_control = &cbuff;
|
||||
msg.msg_controllen = sizeof(cbuff);
|
||||
ret = recvmsg(icmpSocket, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
|
||||
}
|
||||
#endif
|
||||
if (ret == -1)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: RecvMsg Error: %d", err);
|
||||
result.type = -1;
|
||||
result.code = err;
|
||||
return &result;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.msg_flags & MSG_TRUNC)
|
||||
Console.Error("DEV9: ICMP: RecvMsg Truncated");
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
if (msg.msg_flags & MSG_CTRUNC)
|
||||
Console.Error("DEV9: ICMP: RecvMsg Control Truncated");
|
||||
|
||||
sock_extended_err* ex_err = nullptr;
|
||||
cmsghdr* cmsg;
|
||||
|
||||
/* Receive auxiliary data in msgh */
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
|
||||
{
|
||||
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
|
||||
{
|
||||
ex_err = (sock_extended_err*)CMSG_DATA(cmsg);
|
||||
continue;
|
||||
}
|
||||
pxAssert(false);
|
||||
}
|
||||
|
||||
if (ex_err != nullptr)
|
||||
{
|
||||
if (ex_err->ee_origin == SO_EE_ORIGIN_ICMP)
|
||||
{
|
||||
result.type = ex_err->ee_type;
|
||||
result.code = ex_err->ee_code;
|
||||
|
||||
sockaddr_in* sockaddr = (sockaddr_in*)SO_EE_OFFENDER(ex_err);
|
||||
result.address = *(IP_Address*)&sockaddr->sin_addr;
|
||||
return &result;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Recv Error %d", ex_err->ee_errno);
|
||||
result.type = -1;
|
||||
result.code = ex_err->ee_errno;
|
||||
return &result;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int offset;
|
||||
int length;
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
if (icmpConnectionKind == PingType::ICMP)
|
||||
{
|
||||
offset = 0;
|
||||
length = ret;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ip* ipHeader = (ip*)icmpResponseBuffer.get();
|
||||
int headerLength = ipHeader->ip_hl << 2;
|
||||
pxAssert(headerLength == 20);
|
||||
|
||||
offset = headerLength;
|
||||
#ifdef __APPLE__
|
||||
//https://www.unix.com/man-page/mojave/4/ip/
|
||||
//"Note that the ip_off and ip_len fields are in host byte order."
|
||||
//Any other bugs? FreeBSD notes the following
|
||||
//Before FreeBSD 11.0 packets received on raw IP sockets had the ip_len and ip_off fields converted to host byte order.
|
||||
//Before FreeBSD 10.0 packets received on raw IP sockets had the ip_hl sub-tracted from the ip_len field.
|
||||
//TODO, test
|
||||
length = ipHeader->ip_len - headerLength;
|
||||
#else
|
||||
length = ntohs(ipHeader->ip_len) - headerLength;
|
||||
#endif
|
||||
}
|
||||
|
||||
ICMP_Packet icmp(&icmpResponseBuffer[offset], length);
|
||||
PayloadPtr* icmpPayload = static_cast<PayloadPtr*>(icmp.GetPayload());
|
||||
|
||||
result.type = icmp.type;
|
||||
result.code = icmp.code;
|
||||
|
||||
sockaddr_in* sockaddr = (sockaddr_in*)&endpoint;
|
||||
result.address = *(IP_Address*)&sockaddr->sin_addr;
|
||||
|
||||
if (icmp.type == 0)
|
||||
{
|
||||
//Check if response is to us
|
||||
if (icmpConnectionKind == PingType::RAW)
|
||||
{
|
||||
ICMP_HeaderDataIdentifier headerData(icmp.headerData);
|
||||
if (headerData.identifier != icmpId)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//While icmp (and its PayloadPtr) will be destroyed when leaving this function
|
||||
//the data pointed to it persist in icmpResponseBuffer
|
||||
result.dataLength = icmpPayload->GetLength();
|
||||
result.data = icmpPayload->data;
|
||||
return &result;
|
||||
}
|
||||
#if defined(ICMP_SOCKETS_BSD)
|
||||
else if (icmp.type == 3 || icmp.type == 4 || icmp.type == 5 || icmp.type == 11)
|
||||
{
|
||||
//Check if response is to us
|
||||
//We need to extract the sent header
|
||||
IP_Packet ipPacket(icmpPayload->data, icmpPayload->GetLength(), true);
|
||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(ipPacket.GetPayload());
|
||||
ICMP_Packet retIcmp(ipPayload->data, ipPayload->GetLength());
|
||||
|
||||
ICMP_HeaderDataIdentifier headerData(icmp.headerData);
|
||||
if (headerData.identifier != icmpId)
|
||||
return nullptr;
|
||||
|
||||
//This response is for us
|
||||
return &result;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
Console.Error("DEV9: ICMP: Unexpected packet");
|
||||
pxAssert(false);
|
||||
#endif
|
||||
//Assume not for us
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
result.type = -1;
|
||||
result.code = 0;
|
||||
return &result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ICMP_Session::Ping::Send(IP_Address parAdapterIP, IP_Address parDestIP, int parTimeToLive, PayloadPtr* parPayload)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
//Documentation is incorrect, IP_OPTION_INFORMATION is to be used regardless of platform
|
||||
IP_OPTION_INFORMATION ipInfo{0};
|
||||
ipInfo.Ttl = parTimeToLive;
|
||||
DWORD ret;
|
||||
if (parAdapterIP.integer != 0)
|
||||
ret = IcmpSendEcho2Ex(icmpFile, icmpEvent, nullptr, nullptr, parAdapterIP.integer, parDestIP.integer, parPayload->data, parPayload->GetLength(), &ipInfo, icmpResponseBuffer.get(), icmpResponseBufferLen,
|
||||
(DWORD)std::chrono::duration_cast<std::chrono::milliseconds>(ICMP_TIMEOUT).count());
|
||||
else
|
||||
ret = IcmpSendEcho2(icmpFile, icmpEvent, nullptr, nullptr, parDestIP.integer, parPayload->data, parPayload->GetLength(), &ipInfo, icmpResponseBuffer.get(), icmpResponseBufferLen,
|
||||
(DWORD)std::chrono::duration_cast<std::chrono::milliseconds>(ICMP_TIMEOUT).count());
|
||||
|
||||
//Documentation states that IcmpSendEcho2 returns ERROR_IO_PENDING
|
||||
//However, it actully returns zero, with the error set to ERROR_IO_PENDING
|
||||
if (ret == 0)
|
||||
ret = GetLastError();
|
||||
|
||||
if (ret != ERROR_IO_PENDING)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Failed to Send Echo, %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#elif defined(__POSIX__)
|
||||
switch (icmpConnectionKind)
|
||||
{
|
||||
case (PingType::ICMP):
|
||||
case (PingType::RAW):
|
||||
{
|
||||
icmpDeathClockStart = std::chrono::steady_clock::now();
|
||||
|
||||
//broadcast and multicast mignt need extra setsockopts
|
||||
//I don't think any game will broadcast/multicast ping
|
||||
|
||||
if (parAdapterIP.integer != 0)
|
||||
{
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr = parAdapterIP;
|
||||
|
||||
if (bind(icmpSocket, (const sockaddr*)&endpoint, sizeof(endpoint)) == -1)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Failed to bind socket. Error: %d", errno);
|
||||
::close(icmpSocket);
|
||||
icmpSocket == -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
int value = 1;
|
||||
if (setsockopt(icmpSocket, SOL_IP, IP_RECVERR, (char*)&value, sizeof(value)))
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Failed to setsockopt IP_RECVERR. Error: %d", errno);
|
||||
::close(icmpSocket);
|
||||
icmpSocket == -1;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TTL (Note multicast & regular ttl are seperate)
|
||||
if (setsockopt(icmpSocket, IPPROTO_IP, IP_TTL, (const char*)&parTimeToLive, sizeof(parTimeToLive)) == -1)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Failed to set TTL. Error: %d", errno);
|
||||
::close(icmpSocket);
|
||||
icmpSocket == -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ICMP_SOCKETS_LINUX)
|
||||
if (icmpConnectionKind == PingType::ICMP)
|
||||
{
|
||||
//We get assigned a port
|
||||
sockaddr_in endpoint{0};
|
||||
socklen_t endpointsize = sizeof(endpoint);
|
||||
if (getsockname(icmpSocket, (sockaddr*)&endpoint, &endpointsize) == -1)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Failed to get id. Error: %d", errno);
|
||||
::close(icmpSocket);
|
||||
icmpSocket == -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
icmpId = endpoint.sin_port;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
//Use time, in ms, as id
|
||||
icmpId = (u16)std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
ICMP_Packet icmp(parPayload->Clone());
|
||||
icmp.type = 8;
|
||||
icmp.code = 0;
|
||||
|
||||
//We only send one icmp packet per identifier
|
||||
ICMP_HeaderDataIdentifier headerData(icmpId, 1);
|
||||
headerData.WriteHeaderData(icmp.headerData);
|
||||
|
||||
icmp.CalculateChecksum(parAdapterIP, parDestIP);
|
||||
|
||||
std::unique_ptr<u8[]> buffer = std::make_unique<u8[]>(icmp.GetLength());
|
||||
int offset = 0;
|
||||
icmp.WriteBytes(buffer.get(), &offset);
|
||||
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr.s_addr = parDestIP;
|
||||
|
||||
const int ret = sendto(icmpSocket, buffer.get(), icmp.GetLength(), 0, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
if (ret == -1)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Send Error %d", errno);
|
||||
::close(icmpSocket);
|
||||
icmpSocket == -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ICMP_Session::Ping::~Ping()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (icmpFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
IcmpCloseHandle(icmpFile);
|
||||
icmpFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (icmpEvent != NULL)
|
||||
{
|
||||
CloseHandle(icmpEvent);
|
||||
icmpEvent = NULL;
|
||||
}
|
||||
#else
|
||||
if (icmpSocket != -1)
|
||||
{
|
||||
::close(icmpSocket);
|
||||
icmpSocket = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ICMP_Session::ICMP_Session(ConnectionKey parKey, IP_Address parAdapterIP, ThreadSafeMap<Sessions::ConnectionKey, Sessions::BaseSession*>* parConnections)
|
||||
: BaseSession(parKey, parAdapterIP)
|
||||
{
|
||||
connections = parConnections;
|
||||
}
|
||||
|
||||
IP_Payload* ICMP_Session::Recv()
|
||||
{
|
||||
std::unique_lock lock(ping_mutex);
|
||||
|
||||
for (size_t i = 0; i < pings.size(); i++)
|
||||
{
|
||||
ICMP_Session::PingResult* pingRet = nullptr;
|
||||
pingRet = pings[i]->Recv();
|
||||
if (pingRet != nullptr)
|
||||
{
|
||||
Ping* ping = pings[i];
|
||||
//Remove ping from list and unlock
|
||||
pings.erase(pings.begin() + i);
|
||||
lock.unlock();
|
||||
|
||||
//Create return ICMP packet
|
||||
ICMP_Packet* ret = nullptr;
|
||||
if (pingRet->type >= 0)
|
||||
{
|
||||
PayloadData* data;
|
||||
if (pingRet->type == 0)
|
||||
{
|
||||
data = new PayloadData(pingRet->dataLength);
|
||||
memcpy(data->data.get(), pingRet->data, pingRet->dataLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
//We will copy the original packet back here
|
||||
//Allocate fullsize buffer
|
||||
u8* temp = new u8[ping->originalPacket->GetLength()];
|
||||
//Allocate data
|
||||
int responseSize = ping->originalPacket->GetHeaderLength() + 8;
|
||||
data = new PayloadData(responseSize);
|
||||
|
||||
//Write packet back into bytes
|
||||
int offset = 0;
|
||||
ping->originalPacket->WriteBytes(temp, &offset);
|
||||
|
||||
//Copy only needed bytes
|
||||
memcpy(data->data.get(), temp, responseSize);
|
||||
|
||||
//cleanup
|
||||
delete[] temp;
|
||||
}
|
||||
|
||||
ret = new ICMP_Packet(data);
|
||||
ret->type = pingRet->type;
|
||||
ret->code = pingRet->code;
|
||||
memcpy(ret->headerData, ping->headerData, 4);
|
||||
|
||||
if (destIP != pingRet->address)
|
||||
destIP = pingRet->address;
|
||||
}
|
||||
else if (pingRet->type == -1)
|
||||
Console.Error("DEV9: ICMP: Unexpected ICMP status %d", pingRet->code);
|
||||
else
|
||||
DevCon.WriteLn("DEV9: ICMP: ICMP timeout");
|
||||
|
||||
//free ping
|
||||
delete ping;
|
||||
|
||||
if (--open == 0)
|
||||
RaiseEventConnectionClosed();
|
||||
|
||||
if (ret != nullptr)
|
||||
DevCon.WriteLn("DEV9: ICMP: Return Ping");
|
||||
|
||||
//Return packet
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ICMP_Session::Send(PacketReader::IP::IP_Payload* payload)
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Invalid Call");
|
||||
pxAssert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Note, expects caller to set ipTimeToLive before calling
|
||||
bool ICMP_Session::Send(PacketReader::IP::IP_Payload* payload, IP_Packet* packet)
|
||||
{
|
||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(payload);
|
||||
ICMP_Packet icmp(ipPayload->data, ipPayload->GetLength());
|
||||
|
||||
PayloadPtr* icmpPayload = static_cast<PayloadPtr*>(icmp.GetPayload());
|
||||
|
||||
switch (icmp.type)
|
||||
{
|
||||
case 3: //Port Closed
|
||||
switch (icmp.code)
|
||||
{
|
||||
case 3:
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Recived Packet Rejected, Port Closed");
|
||||
|
||||
//RE:Outbreak Hackfix
|
||||
//TODO, check if still needed
|
||||
|
||||
std::unique_ptr<IP_Packet> retPkt;
|
||||
if ((icmpPayload->data[0] & 0xF0) == (4 << 4))
|
||||
retPkt = std::make_unique<IP_Packet>(icmpPayload->data, icmpPayload->GetLength(), true);
|
||||
else
|
||||
{
|
||||
Console.Error("DEV9: ICMP: Malformed ICMP Packet");
|
||||
int off = 1;
|
||||
while ((icmpPayload->data[off] & 0xF0) != (4 << 4))
|
||||
off += 1;
|
||||
|
||||
Console.Error("DEV9: ICMP: Payload delayed %d bytes", off);
|
||||
|
||||
retPkt = std::make_unique<IP_Packet>(&icmpPayload->data[off], icmpPayload->GetLength(), true);
|
||||
}
|
||||
|
||||
IP_Address srvIP = retPkt->sourceIP;
|
||||
u8 prot = retPkt->protocol;
|
||||
u16 srvPort = 0;
|
||||
u16 ps2Port = 0;
|
||||
switch (prot)
|
||||
{
|
||||
case (u8)IP_Type::TCP:
|
||||
case (u8)IP_Type::UDP:
|
||||
//Read ports directly from the payload
|
||||
//both UDP and TCP have the same locations for ports
|
||||
IP_PayloadPtr* payload = static_cast<IP_PayloadPtr*>(retPkt->GetPayload());
|
||||
int offset = 0;
|
||||
NetLib::ReadUInt16(payload->data, &offset, &srvPort); //src
|
||||
NetLib::ReadUInt16(payload->data, &offset, &ps2Port); //dst
|
||||
}
|
||||
|
||||
ConnectionKey Key{0};
|
||||
Key.ip = srvIP;
|
||||
Key.protocol = prot;
|
||||
Key.ps2Port = ps2Port;
|
||||
Key.srvPort = srvPort;
|
||||
|
||||
//is from Normal Port?
|
||||
BaseSession* s = nullptr;
|
||||
connections->TryGetValue(Key, &s);
|
||||
|
||||
if (s != nullptr)
|
||||
{
|
||||
s->Reset();
|
||||
Console.WriteLn("DEV9: ICMP: Reset Rejected Connection");
|
||||
break;
|
||||
}
|
||||
|
||||
//Is from Fixed Port?
|
||||
Key.ip = {0};
|
||||
Key.srvPort = 0;
|
||||
connections->TryGetValue(Key, &s);
|
||||
if (s != nullptr)
|
||||
{
|
||||
s->Reset();
|
||||
Console.WriteLn("DEV9: ICMP: Reset Rejected Connection");
|
||||
break;
|
||||
}
|
||||
|
||||
Console.Error("DEV9: ICMP: Failed To Reset Rejected Connection");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Console.Error("DEV9: ICMP: Unsupported ICMP Code For Destination Unreachable %d", icmp.code);
|
||||
}
|
||||
break;
|
||||
case 8: //Echo
|
||||
{
|
||||
DevCon.WriteLn("DEV9: ICMP: Send Ping");
|
||||
open++;
|
||||
|
||||
Ping* ping = new Ping(icmpPayload->GetLength());
|
||||
|
||||
if (!ping->IsInitialised())
|
||||
{
|
||||
if (--open == 0)
|
||||
RaiseEventConnectionClosed();
|
||||
delete ping;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ping->Send(adapterIP, destIP, packet->timeToLive, icmpPayload))
|
||||
{
|
||||
if (--open == 0)
|
||||
RaiseEventConnectionClosed();
|
||||
delete ping;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ping->headerData, icmp.headerData, 4);
|
||||
|
||||
//Need to copy IP_Packet, original is stack allocated
|
||||
ping->originalPacket = std::make_unique<IP_Packet>(*packet);
|
||||
|
||||
{
|
||||
std::scoped_lock lock(ping_mutex);
|
||||
pings.push_back(ping);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Console.Error("DEV9: ICMP: Unsupported ICMP Type %d", icmp.type);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ICMP_Session::Reset()
|
||||
{
|
||||
RaiseEventConnectionClosed();
|
||||
}
|
||||
|
||||
ICMP_Session::~ICMP_Session()
|
||||
{
|
||||
std::scoped_lock lock(ping_mutex);
|
||||
|
||||
//Cleanup
|
||||
for (size_t i = 0; i < pings.size(); i++)
|
||||
delete pings[i];
|
||||
}
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,100 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "DEV9/SimpleQueue.h"
|
||||
#include "DEV9/ThreadSafeMap.h"
|
||||
#include "DEV9/Sessions/BaseSession.h"
|
||||
#include "DEV9/PacketReader/IP/ICMP/ICMP_Packet.h"
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
class ICMP_Session : public BaseSession
|
||||
{
|
||||
private:
|
||||
struct PingResult
|
||||
{
|
||||
PacketReader::IP::IP_Address address;
|
||||
int type;
|
||||
int code;
|
||||
int dataLength;
|
||||
u8* data;
|
||||
};
|
||||
|
||||
class Ping
|
||||
{
|
||||
public:
|
||||
u8 headerData[4];
|
||||
std::unique_ptr<PacketReader::IP::IP_Packet> originalPacket;
|
||||
|
||||
private:
|
||||
const static std::chrono::duration<std::chrono::steady_clock::rep, std::chrono::steady_clock::period> ICMP_TIMEOUT;
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE icmpFile{INVALID_HANDLE_VALUE};
|
||||
HANDLE icmpEvent{NULL};
|
||||
#elif defined(__POSIX__)
|
||||
enum struct PingType
|
||||
{
|
||||
ICMP,
|
||||
RAW,
|
||||
};
|
||||
|
||||
static PingType icmpConnectionKind;
|
||||
|
||||
//Sockets
|
||||
int icmpSocket{-1};
|
||||
std::chrono::steady_clock::time_point icmpDeathClockStart;
|
||||
u16 icmpId;
|
||||
|
||||
#endif
|
||||
|
||||
//Return buffers
|
||||
PingResult result{0};
|
||||
int icmpResponseBufferLen{0};
|
||||
std::unique_ptr<u8[]> icmpResponseBuffer;
|
||||
|
||||
public:
|
||||
Ping(int requestSize);
|
||||
bool IsInitialised();
|
||||
PingResult* Recv();
|
||||
bool Send(PacketReader::IP::IP_Address parAdapterIP, PacketReader::IP::IP_Address parDestIP, int parTimeToLive, PacketReader::PayloadPtr* parPayload);
|
||||
|
||||
~Ping();
|
||||
};
|
||||
|
||||
SimpleQueue<PacketReader::IP::ICMP::ICMP_Packet*> _recvBuff;
|
||||
std::mutex ping_mutex;
|
||||
std::vector<Ping*> pings;
|
||||
ThreadSafeMap<Sessions::ConnectionKey, Sessions::BaseSession*>* connections;
|
||||
|
||||
std::atomic<int> open{0};
|
||||
|
||||
public:
|
||||
ICMP_Session(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP, ThreadSafeMap<Sessions::ConnectionKey, Sessions::BaseSession*>* parConnections);
|
||||
|
||||
virtual PacketReader::IP::IP_Payload* Recv();
|
||||
virtual bool Send(PacketReader::IP::IP_Payload* payload);
|
||||
bool Send(PacketReader::IP::IP_Payload* payload, PacketReader::IP::IP_Packet* packet);
|
||||
virtual void Reset();
|
||||
|
||||
virtual ~ICMP_Session();
|
||||
};
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,145 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "TCP_Session.h"
|
||||
|
||||
using namespace PacketReader;
|
||||
using namespace PacketReader::IP;
|
||||
using namespace PacketReader::IP::TCP;
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
void TCP_Session::PushRecvBuff(TCP_Packet* tcp)
|
||||
{
|
||||
_recvBuff.Enqueue(tcp);
|
||||
}
|
||||
TCP_Packet* TCP_Session::PopRecvBuff()
|
||||
{
|
||||
TCP_Packet* ret;
|
||||
if (_recvBuff.Dequeue(&ret))
|
||||
return ret;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TCP_Session::IncrementMyNumber(u32 amount)
|
||||
{
|
||||
std::lock_guard numberlock(myNumberSentry);
|
||||
_OldMyNumbers.push_back(_MySequenceNumber);
|
||||
_OldMyNumbers.erase(_OldMyNumbers.begin());
|
||||
|
||||
_MySequenceNumber += amount;
|
||||
}
|
||||
u32 TCP_Session::GetMyNumber()
|
||||
{
|
||||
std::lock_guard numberlock(myNumberSentry);
|
||||
return _MySequenceNumber;
|
||||
}
|
||||
std::tuple<u32, std::vector<u32>> TCP_Session::GetAllMyNumbers()
|
||||
{
|
||||
std::lock_guard numberlock(myNumberSentry);
|
||||
|
||||
std::vector<u32> old;
|
||||
old.reserve(_OldMyNumbers.size());
|
||||
old.insert(old.end(), _OldMyNumbers.begin(), _OldMyNumbers.end());
|
||||
|
||||
return {_MySequenceNumber, old};
|
||||
}
|
||||
void TCP_Session::ResetMyNumbers()
|
||||
{
|
||||
std::lock_guard numberlock(myNumberSentry);
|
||||
_MySequenceNumber = 1;
|
||||
_OldMyNumbers.clear();
|
||||
for (int i = 0; i < oldMyNumCount; i++)
|
||||
_OldMyNumbers.push_back(1);
|
||||
}
|
||||
|
||||
TCP_Session::TCP_Session(ConnectionKey parKey, IP_Address parAdapterIP)
|
||||
: BaseSession(parKey, parAdapterIP)
|
||||
{
|
||||
}
|
||||
|
||||
TCP_Packet* TCP_Session::CreateBasePacket(PayloadData* data)
|
||||
{
|
||||
//DevCon.WriteLn("Creating Base Packet");
|
||||
if (data == nullptr)
|
||||
data = new PayloadData(0);
|
||||
|
||||
TCP_Packet* ret = new TCP_Packet(data);
|
||||
|
||||
//and now to setup THE ENTIRE THING
|
||||
ret->sourcePort = destPort;
|
||||
ret->destinationPort = srcPort;
|
||||
|
||||
ret->sequenceNumber = GetMyNumber();
|
||||
//DevCon.WriteLn("With MySeq: %d", ret->sequenceNumber);
|
||||
ret->acknowledgementNumber = expectedSeqNumber;
|
||||
//DevCon.WriteLn("With MyAck: %d", ret->acknowledgementNumber);
|
||||
|
||||
ret->windowSize = 2 * maxSegmentSize;
|
||||
|
||||
if (sendTimeStamps)
|
||||
{
|
||||
ret->options.push_back(new TCPopNOP());
|
||||
ret->options.push_back(new TCPopNOP());
|
||||
|
||||
const auto timestampChrono = std::chrono::steady_clock::now() - timeStampStart;
|
||||
const u32 timestampSeconds = std::chrono::duration_cast<std::chrono::seconds>(timestampChrono).count() % UINT_MAX;
|
||||
|
||||
ret->options.push_back(new TCPopTS(timestampSeconds, lastRecivedTimeStamp));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TCP_Session::CloseSocket()
|
||||
{
|
||||
if (client != INVALID_SOCKET)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
closesocket(client);
|
||||
#elif defined(__POSIX__)
|
||||
::close(client);
|
||||
#endif
|
||||
client = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP_Session::Reset()
|
||||
{
|
||||
//CloseSocket();
|
||||
RaiseEventConnectionClosed();
|
||||
}
|
||||
|
||||
TCP_Session::~TCP_Session()
|
||||
{
|
||||
CloseSocket();
|
||||
|
||||
//Clear out _recvBuff
|
||||
while (!_recvBuff.IsQueueEmpty())
|
||||
{
|
||||
TCP_Packet* retPay;
|
||||
if (!_recvBuff.Dequeue(&retPay))
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
delete retPay;
|
||||
}
|
||||
}
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,153 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#elif defined(__POSIX__)
|
||||
#define INVALID_SOCKET -1
|
||||
#endif
|
||||
|
||||
#include "DEV9/SimpleQueue.h"
|
||||
#include "DEV9/Sessions/BaseSession.h"
|
||||
#include "DEV9/PacketReader/IP/TCP/TCP_Packet.h"
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
class TCP_Session : public BaseSession
|
||||
{
|
||||
private:
|
||||
enum struct TCP_State
|
||||
{
|
||||
None,
|
||||
SendingSYN_ACK,
|
||||
SentSYN_ACK,
|
||||
Connected,
|
||||
Closing_ClosedByPS2,
|
||||
Closing_ClosedByPS2ThenRemote_WaitingForAck,
|
||||
Closing_ClosedByRemote,
|
||||
Closing_ClosedByRemoteThenPS2_WaitingForAck,
|
||||
CloseCompletedFlushBuffer, //Packets in recvBuff to send
|
||||
CloseCompleted,
|
||||
};
|
||||
enum struct NumCheckResult
|
||||
{
|
||||
OK,
|
||||
GotOldData,
|
||||
Bad
|
||||
};
|
||||
|
||||
SimpleQueue<PacketReader::IP::TCP::TCP_Packet*> _recvBuff;
|
||||
|
||||
#ifdef _WIN32
|
||||
SOCKET client = INVALID_SOCKET;
|
||||
#elif defined(__POSIX__)
|
||||
int client = INVALID_SOCKET;
|
||||
#endif
|
||||
|
||||
TCP_State state = TCP_State::None;
|
||||
|
||||
u16 srcPort = 0;
|
||||
u16 destPort = 0;
|
||||
|
||||
u16 maxSegmentSize = 1460; //Accesed By Both In and Out Threads, but set only on Connect Thread
|
||||
int windowScale = 0;
|
||||
std::atomic<int> windowSize{1460}; //Make atomic instead
|
||||
|
||||
u32 lastRecivedTimeStamp; //Accesed By Both In and Out Threads
|
||||
std::chrono::steady_clock::time_point timeStampStart; //Set By In on connect, read by In and Out Threads, unsure as to correct C++ type
|
||||
bool sendTimeStamps = false; //Accesed By Out Thread Only
|
||||
|
||||
const int receivedPS2SeqNumberCount = 5;
|
||||
u32 expectedSeqNumber; //Accesed By Out Thread Only
|
||||
std::vector<u32> receivedPS2SeqNumbers; //Accesed By Out Thread Only
|
||||
|
||||
std::mutex myNumberSentry;
|
||||
const int oldMyNumCount = 2;
|
||||
u32 _MySequenceNumber = 1;
|
||||
std::vector<u32> _OldMyNumbers;
|
||||
std::atomic<bool> myNumberACKed{true};
|
||||
|
||||
public:
|
||||
TCP_Session(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP);
|
||||
|
||||
virtual PacketReader::IP::IP_Payload* Recv();
|
||||
virtual bool Send(PacketReader::IP::IP_Payload* payload);
|
||||
virtual void Reset();
|
||||
|
||||
virtual ~TCP_Session();
|
||||
|
||||
private:
|
||||
//Async stuff
|
||||
|
||||
void PushRecvBuff(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
PacketReader::IP::TCP::TCP_Packet* PopRecvBuff();
|
||||
|
||||
void IncrementMyNumber(u32 amount);
|
||||
u32 GetMyNumber();
|
||||
std::tuple<u32, std::vector<u32>> GetAllMyNumbers();
|
||||
void ResetMyNumbers();
|
||||
|
||||
NumCheckResult CheckRepeatSYNNumbers(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
NumCheckResult CheckNumbers(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
u32 GetDelta(u32 parExpectedSeq, u32 parGotSeq);
|
||||
//Returns true if errored
|
||||
bool ErrorOnNonEmptyPacket(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
|
||||
//PS2 sent SYN
|
||||
PacketReader::IP::TCP::TCP_Packet* ConnectTCPComplete(bool success);
|
||||
bool SendConnect(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
bool SendConnected(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
|
||||
bool SendData(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
bool SendNoData(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
|
||||
//On Close by PS2
|
||||
//S1: PS2 Sends FIN+ACK
|
||||
//S2: CloseByPS2Stage1_2 sends ACK, state set to Closing_ClosedByPS2
|
||||
//S3: When server closes socket, we send FIN in CloseByPS2Stage3
|
||||
//and set state to Closing_ClosedByPS2ThenRemote_WaitingForAck
|
||||
//S4: PS2 then Sends ACK
|
||||
|
||||
bool CloseByPS2Stage1_2(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
PacketReader::IP::TCP::TCP_Packet* CloseByPS2Stage3();
|
||||
bool CloseByPS2Stage4(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
|
||||
//On Close By Server
|
||||
//S1: CloseByRemoteStage1 sends FIN+ACK, state set to Closing_ClosedByRemote
|
||||
//S2: PS2 Will then sends ACK, this is only checked after stage4
|
||||
//S3: PS2 Will send FIN, possible in the previous ACK packet
|
||||
//S4: CloseByRemoteStage3_4 sends ACK, state set to
|
||||
//Closing_ClosedByRemoteThenPS2_WaitingForAck
|
||||
//We Then Check if S3 has been compleated
|
||||
|
||||
PacketReader::IP::TCP::TCP_Packet* CloseByRemoteStage1();
|
||||
bool CloseByRemoteStage2_ButAfter4(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
bool CloseByRemoteStage3_4(PacketReader::IP::TCP::TCP_Packet* tcp);
|
||||
|
||||
//Error on sending data
|
||||
void CloseByRemoteRST();
|
||||
|
||||
//Returned TCP_Packet Takes ownership of data
|
||||
PacketReader::IP::TCP::TCP_Packet* CreateBasePacket(PacketReader::PayloadData* data = nullptr);
|
||||
|
||||
void CloseSocket();
|
||||
};
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,280 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef __POSIX__
|
||||
#define SOCKET_ERROR -1
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#define SD_RECEIVE SHUT_RD
|
||||
#endif
|
||||
|
||||
#include "TCP_Session.h"
|
||||
|
||||
using namespace PacketReader;
|
||||
using namespace PacketReader::IP;
|
||||
using namespace PacketReader::IP::TCP;
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
PacketReader::IP::IP_Payload* TCP_Session::Recv()
|
||||
{
|
||||
TCP_Packet* ret = PopRecvBuff();
|
||||
if (ret != nullptr)
|
||||
return ret;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TCP_State::SendingSYN_ACK:
|
||||
{
|
||||
fd_set writeSet;
|
||||
fd_set exceptSet;
|
||||
|
||||
FD_ZERO(&writeSet);
|
||||
FD_ZERO(&exceptSet);
|
||||
|
||||
FD_SET(client, &writeSet);
|
||||
FD_SET(client, &exceptSet);
|
||||
|
||||
timeval nowait{0};
|
||||
select(client + 1, nullptr, &writeSet, &exceptSet, &nowait);
|
||||
|
||||
if (FD_ISSET(client, &writeSet))
|
||||
return ConnectTCPComplete(true);
|
||||
if (FD_ISSET(client, &exceptSet))
|
||||
return ConnectTCPComplete(false);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
case TCP_State::SentSYN_ACK:
|
||||
//Don't read data untill PS2 ACKs connection
|
||||
return nullptr;
|
||||
case TCP_State::CloseCompletedFlushBuffer:
|
||||
//When TCP connection is closed by the server
|
||||
//the server is the last to send a packet
|
||||
//so the event must be raised here
|
||||
state = TCP_State::CloseCompleted;
|
||||
RaiseEventConnectionClosed();
|
||||
return nullptr;
|
||||
case TCP_State::Connected:
|
||||
case TCP_State::Closing_ClosedByPS2:
|
||||
//Only accept data in above two states
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint maxSize = 0;
|
||||
if (sendTimeStamps)
|
||||
maxSize = std::min<uint>(maxSegmentSize - 12, windowSize.load());
|
||||
else
|
||||
maxSize = std::min<uint>(maxSegmentSize, windowSize.load());
|
||||
|
||||
if (maxSize != 0 &&
|
||||
myNumberACKed.load())
|
||||
{
|
||||
std::unique_ptr<u8[]> buffer;
|
||||
int err = 0;
|
||||
int recived;
|
||||
|
||||
u_long available;
|
||||
#ifdef _WIN32
|
||||
err = ioctlsocket(client, FIONREAD, &available);
|
||||
#elif defined(__POSIX__)
|
||||
err = ioctl(client, FIONREAD, &available);
|
||||
#endif
|
||||
if (err != SOCKET_ERROR)
|
||||
{
|
||||
if (available > maxSize)
|
||||
Console.WriteLn("DEV9: TCP: Got a lot of data: %d Using: %d", available, maxSize);
|
||||
|
||||
buffer = std::make_unique<u8[]>(maxSize);
|
||||
recived = recv(client, (char*)buffer.get(), maxSize, 0);
|
||||
if (recived == -1)
|
||||
#ifdef _WIN32
|
||||
err = WSAGetLastError();
|
||||
#elif defined(__POSIX__)
|
||||
err = errno;
|
||||
#endif
|
||||
|
||||
switch (err)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case WSAEINVAL:
|
||||
case WSAESHUTDOWN:
|
||||
//In theory, this should only occur when the PS2 has RST the connection
|
||||
//and the call to TCPSession.Recv() occurs at just the right time.
|
||||
|
||||
//Console.WriteLn("DEV9: TCP: Recv() on shutdown socket");
|
||||
return nullptr;
|
||||
case WSAEWOULDBLOCK:
|
||||
return nullptr;
|
||||
#elif defined(__POSIX__)
|
||||
case EINVAL:
|
||||
case ESHUTDOWN:
|
||||
//See WSAESHUTDOWN
|
||||
//Console.WriteLn("DEV9: TCP: Recv() on shutdown socket");
|
||||
return nullptr;
|
||||
case EWOULDBLOCK:
|
||||
return nullptr;
|
||||
#endif
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Recv Error: %d", err);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//Server Closed Socket
|
||||
if (recived == 0)
|
||||
{
|
||||
int result = shutdown(client, SD_RECEIVE);
|
||||
if (result == SOCKET_ERROR)
|
||||
Console.Error("DEV9: TCP: Shutdown SD_RECEIVE Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TCP_State::Connected:
|
||||
return CloseByRemoteStage1();
|
||||
case TCP_State::Closing_ClosedByPS2:
|
||||
return CloseByPS2Stage3();
|
||||
default:
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Remote Close In Invalid State");
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
DevCon.WriteLn("DEV9: TCP: [SRV]Sending %d bytes", recived);
|
||||
|
||||
PayloadData* recivedData = new PayloadData(recived);
|
||||
memcpy(recivedData->data.get(), buffer.get(), recived);
|
||||
|
||||
TCP_Packet* iRet = CreateBasePacket(recivedData);
|
||||
IncrementMyNumber((u32)recived);
|
||||
|
||||
iRet->SetACK(true);
|
||||
iRet->SetPSH(true);
|
||||
|
||||
myNumberACKed.store(false);
|
||||
//DevCon.WriteLn("DEV9: TCP: myNumberACKed Reset");
|
||||
return iRet;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TCP_Packet* TCP_Session::ConnectTCPComplete(bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
state = TCP_State::SentSYN_ACK;
|
||||
|
||||
TCP_Packet* ret = new TCP_Packet(new PayloadData(0));
|
||||
//Return the fact we connected
|
||||
ret->sourcePort = destPort;
|
||||
ret->destinationPort = srcPort;
|
||||
|
||||
ret->sequenceNumber = GetMyNumber();
|
||||
IncrementMyNumber(1);
|
||||
|
||||
ret->acknowledgementNumber = expectedSeqNumber;
|
||||
|
||||
ret->SetSYN(true);
|
||||
ret->SetACK(true);
|
||||
ret->windowSize = (2 * maxSegmentSize);
|
||||
ret->options.push_back(new TCPopMSS(maxSegmentSize));
|
||||
|
||||
ret->options.push_back(new TCPopNOP());
|
||||
ret->options.push_back(new TCPopWS(0));
|
||||
|
||||
if (sendTimeStamps)
|
||||
{
|
||||
ret->options.push_back(new TCPopNOP());
|
||||
ret->options.push_back(new TCPopNOP());
|
||||
|
||||
const auto timestampChrono = std::chrono::steady_clock::now() - timeStampStart;
|
||||
const u32 timestampSeconds = std::chrono::duration_cast<std::chrono::seconds>(timestampChrono).count() % UINT_MAX;
|
||||
|
||||
ret->options.push_back(new TCPopTS(timestampSeconds, lastRecivedTimeStamp));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
int error = 0;
|
||||
#ifdef _WIN32
|
||||
int len = sizeof(error);
|
||||
if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
|
||||
Console.Error("DEV9: TCP: Unkown TCP Connection Error (getsockopt Error: %d)", WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
|
||||
Console.Error("DEV9: TCP: Unkown TCP Connection Error (getsockopt Error: %d)", errno);
|
||||
#endif
|
||||
else
|
||||
Console.Error("DEV9: TCP: Send Error: %d", error);
|
||||
|
||||
state = TCP_State::CloseCompleted;
|
||||
RaiseEventConnectionClosed();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
PacketReader::IP::TCP::TCP_Packet* TCP_Session::CloseByPS2Stage3()
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: Remote has closed connection after PS2");
|
||||
|
||||
TCP_Packet* ret = CreateBasePacket();
|
||||
IncrementMyNumber(1);
|
||||
|
||||
ret->SetACK(true);
|
||||
ret->SetFIN(true);
|
||||
|
||||
myNumberACKed.store(false);
|
||||
//DevCon.WriteLn("myNumberACKed Reset");
|
||||
|
||||
state = TCP_State::Closing_ClosedByPS2ThenRemote_WaitingForAck;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PacketReader::IP::TCP::TCP_Packet* TCP_Session::CloseByRemoteStage1()
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: Remote has closed connection");
|
||||
|
||||
TCP_Packet* ret = CreateBasePacket();
|
||||
IncrementMyNumber(1);
|
||||
|
||||
ret->SetACK(true);
|
||||
ret->SetFIN(true);
|
||||
|
||||
myNumberACKed.store(false);
|
||||
//DevCon.WriteLn("myNumberACKed Reset");
|
||||
|
||||
state = TCP_State::Closing_ClosedByRemote;
|
||||
return ret;
|
||||
}
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,637 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __POSIX__
|
||||
#define SOCKET_ERROR -1
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#define SD_RECEIVE SHUT_RD
|
||||
#define SD_SEND SHUT_WR
|
||||
#endif
|
||||
|
||||
#include "TCP_Session.h"
|
||||
|
||||
using namespace PacketReader;
|
||||
using namespace PacketReader::IP;
|
||||
using namespace PacketReader::IP::TCP;
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
bool TCP_Session::Send(PacketReader::IP::IP_Payload* payload)
|
||||
{
|
||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(payload);
|
||||
TCP_Packet tcp(ipPayload->data, ipPayload->GetLength());
|
||||
|
||||
if (destPort != 0)
|
||||
{
|
||||
if (!(tcp.destinationPort == destPort && tcp.sourcePort == srcPort))
|
||||
{
|
||||
Console.Error("DEV9: TCP: Packet invalid for current session (Duplicate key?)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Maybe untested
|
||||
if (tcp.GetRST() == true)
|
||||
{
|
||||
//DevCon.Writeln("DEV9: TCP: PS2 has reset connection");
|
||||
//PS2 has reset connection;
|
||||
if (client != INVALID_SOCKET)
|
||||
CloseSocket();
|
||||
else
|
||||
Console.Error("DEV9: TCP: RESET CLOSED CONNECTION");
|
||||
//PS2 sent RST
|
||||
//clearly not expecting
|
||||
//more data
|
||||
state = TCP_State::CloseCompleted;
|
||||
RaiseEventConnectionClosed();
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TCP_State::None:
|
||||
return SendConnect(&tcp);
|
||||
case TCP_State::SendingSYN_ACK:
|
||||
if (CheckRepeatSYNNumbers(&tcp) == NumCheckResult::Bad)
|
||||
{
|
||||
Console.Error("DEV9: TCP: Invalid Repeated SYN (SendingSYN_ACK)");
|
||||
return false;
|
||||
}
|
||||
return true; //Ignore reconnect attempts while we are still attempting connection
|
||||
case TCP_State::SentSYN_ACK:
|
||||
return SendConnected(&tcp);
|
||||
case TCP_State::Connected:
|
||||
if (tcp.GetFIN() == true) //Connection Close Part 1, received FIN from PS2
|
||||
return CloseByPS2Stage1_2(&tcp);
|
||||
else
|
||||
return SendData(&tcp);
|
||||
|
||||
case TCP_State::Closing_ClosedByPS2:
|
||||
return SendNoData(&tcp);
|
||||
case TCP_State::Closing_ClosedByPS2ThenRemote_WaitingForAck:
|
||||
return CloseByPS2Stage4(&tcp);
|
||||
|
||||
case TCP_State::Closing_ClosedByRemote:
|
||||
if (tcp.GetFIN() == true) //Connection Close Part 3, received FIN from PS2
|
||||
return CloseByRemoteStage3_4(&tcp);
|
||||
|
||||
return SendData(&tcp);
|
||||
case TCP_State::Closing_ClosedByRemoteThenPS2_WaitingForAck:
|
||||
return CloseByRemoteStage2_ButAfter4(&tcp);
|
||||
case TCP_State::CloseCompleted:
|
||||
Console.Error("DEV9: TCP: Attempt to send to a closed TCP connection");
|
||||
return false;
|
||||
default:
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Invalid TCP State");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//PS2 sent SYN
|
||||
bool TCP_Session::SendConnect(TCP_Packet* tcp)
|
||||
{
|
||||
//Expect SYN Packet
|
||||
destPort = tcp->destinationPort;
|
||||
srcPort = tcp->sourcePort;
|
||||
|
||||
if (tcp->GetSYN() == false)
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Attempt To Send Data On Non Connected Connection");
|
||||
return true;
|
||||
}
|
||||
expectedSeqNumber = tcp->sequenceNumber + 1;
|
||||
//Fill out last received numbers
|
||||
receivedPS2SeqNumbers.clear();
|
||||
for (int i = 0; i < receivedPS2SeqNumberCount; i++)
|
||||
receivedPS2SeqNumbers.push_back(tcp->sequenceNumber);
|
||||
|
||||
ResetMyNumbers();
|
||||
|
||||
for (size_t i = 0; i < tcp->options.size(); i++)
|
||||
{
|
||||
switch (tcp->options[i]->GetCode())
|
||||
{
|
||||
case 0: //End
|
||||
case 1: //Nop
|
||||
continue;
|
||||
case 2: //MSS
|
||||
maxSegmentSize = ((TCPopMSS*)(tcp->options[i]))->maxSegmentSize;
|
||||
break;
|
||||
case 3: //WindowScale
|
||||
windowScale = ((TCPopWS*)(tcp->options[i]))->windowScale;
|
||||
if (windowScale > 0)
|
||||
Console.Error("DEV9: TCP: Non-Zero WindowScale Option");
|
||||
break;
|
||||
case 8: //TimeStamp
|
||||
lastRecivedTimeStamp = ((TCPopTS*)(tcp->options[i]))->senderTimeStamp;
|
||||
sendTimeStamps = true;
|
||||
timeStampStart = std::chrono::steady_clock::now();
|
||||
break;
|
||||
default:
|
||||
Console.Error("DEV9: TCP: Got Unknown Option %d", tcp->options[i]->GetCode());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
windowSize.store(tcp->windowSize << windowScale);
|
||||
|
||||
CloseSocket();
|
||||
|
||||
//client = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (client == INVALID_SOCKET)
|
||||
{
|
||||
Console.Error("DEV9: TCP: Failed to open socket. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
RaiseEventConnectionClosed();
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret;
|
||||
if (adapterIP.integer != 0)
|
||||
{
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr = adapterIP;
|
||||
|
||||
ret = bind(client, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
|
||||
if (ret != 0)
|
||||
Console.Error("DEV9: UDP: Failed to bind socket. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
u_long blocking = 1;
|
||||
ret = ioctlsocket(client, FIONBIO, &blocking);
|
||||
#elif defined(__POSIX__)
|
||||
int blocking = 1;
|
||||
ret = ioctl(client, FIONBIO, &blocking);
|
||||
#endif
|
||||
|
||||
if (ret != 0)
|
||||
Console.Error("DEV9: TCP: Failed to set non blocking. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
|
||||
|
||||
const int noDelay = true; //BOOL
|
||||
ret = setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay));
|
||||
|
||||
if (ret != 0)
|
||||
Console.Error("DEV9: TCP: Failed to set TCP_NODELAY. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr = destIP;
|
||||
endpoint.sin_port = htons(destPort);
|
||||
|
||||
ret = connect(client, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const int err = WSAGetLastError();
|
||||
if (err != WSAEWOULDBLOCK)
|
||||
#elif defined(__POSIX__)
|
||||
const int err = errno;
|
||||
if (err != EWOULDBLOCK && err != EINPROGRESS)
|
||||
#endif
|
||||
{
|
||||
Console.Error("DEV9: TCP: Failed to connect socket. Error: %d", err);
|
||||
RaiseEventConnectionClosed();
|
||||
return false;
|
||||
}
|
||||
//Compleation of socket connection checked in recv
|
||||
}
|
||||
|
||||
state = TCP_State::SendingSYN_ACK;
|
||||
return true;
|
||||
}
|
||||
|
||||
//PS2 responding to our SYN-ACK (by sending ACK)
|
||||
bool TCP_Session::SendConnected(TCP_Packet* tcp)
|
||||
{
|
||||
if (tcp->GetSYN() == true)
|
||||
{
|
||||
if (CheckRepeatSYNNumbers(tcp) == NumCheckResult::Bad)
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Invalid Repeated SYN (SentSYN_ACK)");
|
||||
return true;
|
||||
}
|
||||
return true; //Ignore reconnect attempts while we are still attempting connection
|
||||
}
|
||||
const NumCheckResult Result = CheckNumbers(tcp);
|
||||
if (Result == NumCheckResult::Bad)
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Bad TCP Numbers Received");
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tcp->options.size(); i++)
|
||||
{
|
||||
switch (tcp->options[i]->GetCode())
|
||||
{
|
||||
case 0: //End
|
||||
case 1: //Nop
|
||||
continue;
|
||||
case 8: //Timestamp
|
||||
lastRecivedTimeStamp = ((TCPopTS*)(tcp->options[i]))->senderTimeStamp;
|
||||
break;
|
||||
default:
|
||||
Console.Error("DEV9: TCP: Got Unknown Option %d", tcp->options[i]->GetCode());
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Next packet will be data
|
||||
state = TCP_State::Connected;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCP_Session::SendData(TCP_Packet* tcp)
|
||||
{
|
||||
if (tcp->GetSYN())
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Attempt to Connect to an open Port");
|
||||
return true;
|
||||
}
|
||||
if (tcp->GetURG())
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Urgent Data Not Supported");
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < tcp->options.size(); i++)
|
||||
{
|
||||
switch (tcp->options[i]->GetCode())
|
||||
{
|
||||
case 0: //End
|
||||
case 1: //Nop
|
||||
continue;
|
||||
case 8:
|
||||
lastRecivedTimeStamp = ((TCPopTS*)(tcp->options[i]))->senderTimeStamp;
|
||||
break;
|
||||
default:
|
||||
Console.Error("DEV9: TCP: Got Unknown Option %d", tcp->options[i]->GetCode());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
windowSize.store(tcp->windowSize << windowScale);
|
||||
|
||||
const NumCheckResult Result = CheckNumbers(tcp);
|
||||
const uint delta = GetDelta(expectedSeqNumber, tcp->sequenceNumber);
|
||||
//if (Result == NumCheckResult::GotOldData)
|
||||
//{
|
||||
// DevCon.WriteLn("[PS2] New Data Offset: %d bytes", delta);
|
||||
// DevCon.WriteLn("[PS2] New Data Length: %d bytes", ((uint)tcp->GetPayload()->GetLength() - delta));
|
||||
//}
|
||||
if (Result == NumCheckResult::Bad)
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Bad TCP Numbers Received");
|
||||
return true;
|
||||
}
|
||||
if (tcp->GetPayload()->GetLength() != 0)
|
||||
{
|
||||
if (tcp->GetPayload()->GetLength() - delta > 0)
|
||||
{
|
||||
DevCon.WriteLn("DEV9: TCP: [PS2] Sending: %d bytes", tcp->GetPayload()->GetLength());
|
||||
|
||||
receivedPS2SeqNumbers.erase(receivedPS2SeqNumbers.begin());
|
||||
receivedPS2SeqNumbers.push_back(expectedSeqNumber);
|
||||
|
||||
//Send the Data
|
||||
int sent = 0;
|
||||
PayloadPtr* payload = static_cast<PayloadPtr*>(tcp->GetPayload());
|
||||
while (sent != payload->GetLength())
|
||||
{
|
||||
int ret = send(client, (const char*)&payload->data[sent], payload->GetLength() - sent, 0);
|
||||
|
||||
if (sent == SOCKET_ERROR)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const int err = WSAGetLastError();
|
||||
if (err == WSAEWOULDBLOCK)
|
||||
#elif defined(__POSIX__)
|
||||
const int err = errno;
|
||||
if (err == EWOULDBLOCK)
|
||||
#endif
|
||||
std::this_thread::yield();
|
||||
else
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Send Error: %d", err);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
sent += ret;
|
||||
}
|
||||
|
||||
expectedSeqNumber += ((uint)tcp->GetPayload()->GetLength() - delta);
|
||||
//Done send
|
||||
}
|
||||
//ACK data
|
||||
//DevCon.WriteLn("[SRV] ACK Data: %d", expectedSeqNumber);
|
||||
TCP_Packet* ret = CreateBasePacket();
|
||||
ret->SetACK(true);
|
||||
|
||||
PushRecvBuff(ret);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCP_Session::SendNoData(TCP_Packet* tcp)
|
||||
{
|
||||
if (tcp->GetSYN() == true)
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Attempt to Connect to an open Port");
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < tcp->options.size(); i++)
|
||||
{
|
||||
switch (tcp->options[i]->GetCode())
|
||||
{
|
||||
case 0: //End
|
||||
case 1: //Nop
|
||||
continue;
|
||||
case 8:
|
||||
lastRecivedTimeStamp = ((TCPopTS*)(tcp->options[i]))->senderTimeStamp;
|
||||
break;
|
||||
default:
|
||||
Console.Error("DEV9: TCP: Got Unknown Option %d", tcp->options[i]->GetCode());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOnNonEmptyPacket(tcp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TCP_Session::NumCheckResult TCP_Session::CheckRepeatSYNNumbers(TCP_Packet* tcp)
|
||||
{
|
||||
//DevCon.WriteLn("DEV9: TCP: CHECK_REPEAT_SYN_NUMBERS");
|
||||
//DevCon.WriteLn("DEV9: TCP: [SRV]CurrAckNumber = %d [PS2]Seq Number = %d", expectedSeqNumber, tcp->sequenceNumber);
|
||||
|
||||
if (tcp->sequenceNumber != expectedSeqNumber - 1)
|
||||
{
|
||||
Console.Error("DEV9: TCP: [PS2] Sent Unexpected Sequence Number From Repeated SYN Packet, Got %d Expected %d", tcp->sequenceNumber, (expectedSeqNumber - 1));
|
||||
return NumCheckResult::Bad;
|
||||
}
|
||||
return NumCheckResult::OK;
|
||||
}
|
||||
|
||||
TCP_Session::NumCheckResult TCP_Session::CheckNumbers(TCP_Packet* tcp)
|
||||
{
|
||||
u32 seqNum;
|
||||
std::vector<u32> oldSeqNums;
|
||||
std::tie(seqNum, oldSeqNums) = GetAllMyNumbers();
|
||||
|
||||
//DevCon.WriteLn("DEV9: TCP: CHECK_NUMBERS");
|
||||
//DevCon.WriteLn("DEV9: TCP: [SRV]CurrSeqNumber = %d [PS2]Ack Number = %d", seqNum, tcp->acknowledgementNumber);
|
||||
//DevCon.WriteLn("DEV9: TCP: [SRV]CurrAckNumber = %d [PS2]Seq Number = %d", expectedSeqNumber, tcp->sequenceNumber);
|
||||
//DevCon.WriteLn("DEV9: TCP: [PS2]Data Length = %d", tcp->GetPayload()->GetLength());
|
||||
|
||||
if (tcp->acknowledgementNumber != seqNum)
|
||||
{
|
||||
//DevCon.WriteLn("DEV9: TCP: [PS2]Sent Outdated Acknowledgement Number, Got %d Expected %d", tcp->acknowledgementNumber, seqNum);
|
||||
|
||||
//Check if oldSeqNums contains tcp->acknowledgementNumber
|
||||
if (std::find(oldSeqNums.begin(), oldSeqNums.end(), tcp->acknowledgementNumber) == oldSeqNums.end())
|
||||
{
|
||||
Console.Error("DEV9: TCP: [PS2] Sent Unexpected Acknowledgement Number, did not Match Old Numbers, Got %d Expected %d", tcp->acknowledgementNumber, seqNum);
|
||||
return NumCheckResult::Bad;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//DevCon.WriteLn("[PS2]CurrSeqNumber Acknowleged By PS2");
|
||||
myNumberACKed.store(true);
|
||||
}
|
||||
|
||||
if (tcp->sequenceNumber != expectedSeqNumber)
|
||||
{
|
||||
if (tcp->GetPayload()->GetLength() == 0)
|
||||
{
|
||||
Console.Error("DEV9: TCP: [PS2] Sent Unexpected Sequence Number From ACK Packet, Got %d Expected %d", tcp->sequenceNumber, expectedSeqNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Check if receivedPS2SeqNumbers contains tcp->sequenceNumber
|
||||
if (std::find(receivedPS2SeqNumbers.begin(), receivedPS2SeqNumbers.end(), tcp->sequenceNumber) == receivedPS2SeqNumbers.end())
|
||||
{
|
||||
Console.Error("DEV9: TCP: [PS2] Sent an Old Seq Number on an Data packet, Got %d Expected %d", tcp->sequenceNumber, expectedSeqNumber);
|
||||
return NumCheckResult::GotOldData;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("DEV9: TCP: [PS2] Sent Unexpected Sequence Number From Data Packet, Got %d Expected %d", tcp->sequenceNumber, expectedSeqNumber);
|
||||
return NumCheckResult::Bad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NumCheckResult::OK;
|
||||
}
|
||||
u32 TCP_Session::GetDelta(u32 parExpectedSeq, u32 parGotSeq)
|
||||
{
|
||||
u32 delta = parExpectedSeq - parGotSeq;
|
||||
if (delta > 0.5 * UINT_MAX)
|
||||
{
|
||||
delta = UINT_MAX - parExpectedSeq + parGotSeq;
|
||||
Console.Error("DEV9: TCP: [PS2] SequenceNumber Overflow Detected");
|
||||
Console.Error("DEV9: TCP: [PS2] New Data Offset: %d bytes", delta);
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
bool TCP_Session::ErrorOnNonEmptyPacket(TCP_Packet* tcp)
|
||||
{
|
||||
NumCheckResult ResultFIN = CheckNumbers(tcp);
|
||||
if (ResultFIN == NumCheckResult::GotOldData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ResultFIN == NumCheckResult::Bad)
|
||||
{
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Bad TCP Numbers Received");
|
||||
return true;
|
||||
}
|
||||
if (tcp->GetPayload()->GetLength() > 0)
|
||||
{
|
||||
uint delta = GetDelta(expectedSeqNumber, tcp->sequenceNumber);
|
||||
if (delta == 0)
|
||||
return false;
|
||||
|
||||
CloseByRemoteRST();
|
||||
Console.Error("DEV9: TCP: Invalid Packet, Packet Has Data");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Connection Closing Finished in CloseByPS2Stage4
|
||||
bool TCP_Session::CloseByPS2Stage1_2(TCP_Packet* tcp)
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: PS2 has closed connection");
|
||||
|
||||
if (ErrorOnNonEmptyPacket(tcp)) //Sending FIN with data
|
||||
return true;
|
||||
|
||||
receivedPS2SeqNumbers.erase(receivedPS2SeqNumbers.begin());
|
||||
receivedPS2SeqNumbers.push_back(expectedSeqNumber);
|
||||
expectedSeqNumber += 1;
|
||||
|
||||
state = TCP_State::Closing_ClosedByPS2;
|
||||
|
||||
const int result = shutdown(client, SD_SEND);
|
||||
if (result == SOCKET_ERROR)
|
||||
Console.Error("DEV9: TCP: Shutdown SD_SEND Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
|
||||
//Connection Close Part 2, Send ACK to PS2
|
||||
TCP_Packet* ret = CreateBasePacket();
|
||||
|
||||
ret->SetACK(true);
|
||||
PushRecvBuff(ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//PS2 responding to server response to PS2 Closing connection
|
||||
bool TCP_Session::CloseByPS2Stage4(TCP_Packet* tcp)
|
||||
{
|
||||
//Close Part 4, Receive ACK from PS2
|
||||
//Console.WriteLn("DEV9: TCP: Completed Close By PS2");
|
||||
|
||||
if (ErrorOnNonEmptyPacket(tcp))
|
||||
return true;
|
||||
|
||||
if (myNumberACKed.load())
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: ACK was for FIN");
|
||||
CloseSocket();
|
||||
state = TCP_State::CloseCompleted;
|
||||
//recv buffer should be empty
|
||||
RaiseEventConnectionClosed();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCP_Session::CloseByRemoteStage2_ButAfter4(TCP_Packet* tcp)
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: Completed Close By PS2");
|
||||
|
||||
if (ErrorOnNonEmptyPacket(tcp))
|
||||
return true;
|
||||
|
||||
if (myNumberACKed.load())
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: ACK was for FIN");
|
||||
CloseSocket();
|
||||
state = TCP_State::CloseCompletedFlushBuffer;
|
||||
//Recive buffer may not be empty
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCP_Session::CloseByRemoteStage3_4(TCP_Packet* tcp)
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: PS2 has closed connection after remote");
|
||||
|
||||
if (ErrorOnNonEmptyPacket(tcp))
|
||||
return true;
|
||||
|
||||
receivedPS2SeqNumbers.erase(receivedPS2SeqNumbers.begin());
|
||||
receivedPS2SeqNumbers.push_back(expectedSeqNumber);
|
||||
expectedSeqNumber += 1;
|
||||
|
||||
int result = shutdown(client, SD_SEND);
|
||||
if (result == SOCKET_ERROR)
|
||||
Console.Error("DEV9: TCP: Shutdown SD_SEND Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
|
||||
TCP_Packet* ret = CreateBasePacket();
|
||||
|
||||
ret->SetACK(true);
|
||||
|
||||
PushRecvBuff(ret);
|
||||
|
||||
if (myNumberACKed.load())
|
||||
{
|
||||
//Console.WriteLn("DEV9: TCP: ACK was for FIN");
|
||||
CloseSocket();
|
||||
state = TCP_State::CloseCompletedFlushBuffer;
|
||||
//Recive buffer may not be empty
|
||||
}
|
||||
else
|
||||
state = TCP_State::Closing_ClosedByRemoteThenPS2_WaitingForAck;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Error on sending data
|
||||
void TCP_Session::CloseByRemoteRST()
|
||||
{
|
||||
TCP_Packet* reterr = CreateBasePacket();
|
||||
reterr->SetRST(true);
|
||||
PushRecvBuff(reterr);
|
||||
|
||||
CloseSocket();
|
||||
state = TCP_State::CloseCompletedFlushBuffer;
|
||||
}
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,32 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DEV9/Sessions/BaseSession.h"
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
class UDP_BaseSession : public BaseSession
|
||||
{
|
||||
public:
|
||||
UDP_BaseSession(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP)
|
||||
: BaseSession(parKey, parAdapterIP)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool WillRecive(PacketReader::IP::IP_Address parDestIP) = 0;
|
||||
};
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,238 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifdef __POSIX__
|
||||
#define SOCKET_ERROR -1
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "UDP_FixedPort.h"
|
||||
#include "DEV9/PacketReader/IP/UDP/UDP_Packet.h"
|
||||
|
||||
using namespace PacketReader;
|
||||
using namespace PacketReader::IP;
|
||||
using namespace PacketReader::IP::UDP;
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
UDP_FixedPort::UDP_FixedPort(ConnectionKey parKey, IP_Address parAdapterIP, u16 parPort)
|
||||
: BaseSession(parKey, parAdapterIP)
|
||||
, client{socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}
|
||||
, port(parPort)
|
||||
{
|
||||
int ret;
|
||||
if (client == INVALID_SOCKET)
|
||||
{
|
||||
Console.Error("DEV9: UDP: Failed to open socket. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
//RaiseEventConnectionClosed(); //TODO
|
||||
return;
|
||||
}
|
||||
|
||||
const int reuseAddress = true; //BOOL
|
||||
ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddress, sizeof(reuseAddress));
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
Console.Error("DEV9: UDP: Failed to set SO_REUSEADDR. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
|
||||
const int broadcastEnable = true; //BOOL
|
||||
ret = setsockopt(client, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcastEnable, sizeof(broadcastEnable));
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
Console.Error("DEV9: UDP: Failed to set SO_BROADCAST. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
IP_Payload* UDP_FixedPort::Recv()
|
||||
{
|
||||
if (!open.load())
|
||||
return nullptr;
|
||||
|
||||
int ret;
|
||||
fd_set sReady;
|
||||
fd_set sExcept;
|
||||
|
||||
timeval nowait{0};
|
||||
FD_ZERO(&sReady);
|
||||
FD_ZERO(&sExcept);
|
||||
FD_SET(client, &sReady);
|
||||
FD_SET(client, &sExcept);
|
||||
ret = select(client + 1, &sReady, nullptr, &sExcept, &nowait);
|
||||
|
||||
bool hasData;
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
hasData = false;
|
||||
Console.WriteLn("DEV9: UDP: select failed. Error Code: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
}
|
||||
else if (FD_ISSET(client, &sExcept))
|
||||
{
|
||||
hasData = false;
|
||||
|
||||
int error = 0;
|
||||
#ifdef _WIN32
|
||||
int len = sizeof(error);
|
||||
if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
|
||||
Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
|
||||
Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", errno);
|
||||
#endif
|
||||
else
|
||||
Console.Error("DEV9: UDP: Recv Error: %d", error);
|
||||
}
|
||||
else
|
||||
hasData = FD_ISSET(client, &sReady);
|
||||
|
||||
if (hasData)
|
||||
{
|
||||
u_long available = 0;
|
||||
PayloadData* recived = nullptr;
|
||||
std::unique_ptr<u8[]> buffer;
|
||||
sockaddr endpoint{0};
|
||||
|
||||
//FIONREAD returns total size of all available messages
|
||||
//but we will read one message at a time
|
||||
#ifdef _WIN32
|
||||
ret = ioctlsocket(client, FIONREAD, &available);
|
||||
#elif defined(__POSIX__)
|
||||
ret = ioctl(client, FIONREAD, &available);
|
||||
#endif
|
||||
if (ret != SOCKET_ERROR)
|
||||
{
|
||||
buffer = std::make_unique<u8[]>(available);
|
||||
|
||||
#ifdef _WIN32
|
||||
int fromlen = sizeof(endpoint);
|
||||
#elif defined(__POSIX__)
|
||||
socklen_t fromlen = sizeof(endpoint);
|
||||
#endif
|
||||
ret = recvfrom(client, (char*)buffer.get(), available, 0, &endpoint, &fromlen);
|
||||
}
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
Console.Error("UDP Recv Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
RaiseEventConnectionClosed();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
recived = new PayloadData(ret);
|
||||
memcpy(recived->data.get(), buffer.get(), ret);
|
||||
|
||||
UDP_Packet* iRet = new UDP_Packet(recived);
|
||||
iRet->destinationPort = port;
|
||||
|
||||
sockaddr_in* sockaddr = (sockaddr_in*)&endpoint;
|
||||
destIP = *(IP_Address*)&sockaddr->sin_addr;
|
||||
iRet->sourcePort = ntohs(sockaddr->sin_port);
|
||||
{
|
||||
std::lock_guard numberlock(connectionSentry);
|
||||
|
||||
for (size_t i = 0; i < connections.size(); i++)
|
||||
{
|
||||
UDP_BaseSession* s = connections[i];
|
||||
if (s->WillRecive(destIP))
|
||||
return iRet;
|
||||
}
|
||||
}
|
||||
Console.Error("DEV9: UDP: Unexpected packet, dropping");
|
||||
delete iRet;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UDP_FixedPort::Send(PacketReader::IP::IP_Payload* payload)
|
||||
{
|
||||
pxAssert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
void UDP_FixedPort::Reset()
|
||||
{
|
||||
std::lock_guard numberlock(connectionSentry);
|
||||
|
||||
for (size_t i = 0; i < connections.size(); i++)
|
||||
connections[i]->Reset();
|
||||
}
|
||||
|
||||
UDP_Session* UDP_FixedPort::NewClientSession(ConnectionKey parNewKey, bool parIsBrodcast, bool parIsMulticast)
|
||||
{
|
||||
UDP_Session* s = new UDP_Session(parNewKey, adapterIP, parIsBrodcast, parIsMulticast, client);
|
||||
|
||||
s->AddConnectionClosedHandler([&](BaseSession* session) { HandleChildConnectionClosed(session); });
|
||||
|
||||
{
|
||||
std::lock_guard numberlock(connectionSentry);
|
||||
connections.push_back(s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void UDP_FixedPort::HandleChildConnectionClosed(BaseSession* sender)
|
||||
{
|
||||
std::lock_guard numberlock(connectionSentry);
|
||||
|
||||
auto index = std::find(connections.begin(), connections.end(), sender);
|
||||
if (index != connections.end())
|
||||
{
|
||||
connections.erase(index);
|
||||
if (connections.size() == 0)
|
||||
RaiseEventConnectionClosed();
|
||||
}
|
||||
}
|
||||
|
||||
UDP_FixedPort::~UDP_FixedPort()
|
||||
{
|
||||
open.store(false);
|
||||
if (client != INVALID_SOCKET)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
closesocket(client);
|
||||
#elif defined(__POSIX__)
|
||||
::close(client);
|
||||
#endif
|
||||
client = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,64 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#elif defined(__POSIX__)
|
||||
#define INVALID_SOCKET -1
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include "DEV9/Sessions/BaseSession.h"
|
||||
#include "UDP_BaseSession.h"
|
||||
#include "UDP_Session.h"
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
class UDP_FixedPort : public BaseSession
|
||||
{
|
||||
private:
|
||||
std::atomic<bool> open{true};
|
||||
|
||||
#ifdef _WIN32
|
||||
SOCKET client = INVALID_SOCKET;
|
||||
#elif defined(__POSIX__)
|
||||
int client = INVALID_SOCKET;
|
||||
#endif
|
||||
|
||||
public:
|
||||
const u16 port = 0;
|
||||
|
||||
private:
|
||||
std::mutex connectionSentry;
|
||||
std::vector<UDP_BaseSession*> connections;
|
||||
|
||||
public:
|
||||
UDP_FixedPort(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP, u16 parPort);
|
||||
|
||||
virtual PacketReader::IP::IP_Payload* Recv();
|
||||
virtual bool Send(PacketReader::IP::IP_Payload* payload);
|
||||
virtual void Reset();
|
||||
|
||||
UDP_Session* NewClientSession(ConnectionKey parNewKey, bool parIsBrodcast, bool parIsMulticast);
|
||||
|
||||
virtual ~UDP_FixedPort();
|
||||
|
||||
private:
|
||||
void HandleChildConnectionClosed(BaseSession* sender);
|
||||
};
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,401 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifdef __POSIX__
|
||||
#define SOCKET_ERROR -1
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "UDP_Session.h"
|
||||
#include "DEV9/PacketReader/IP/UDP/UDP_Packet.h"
|
||||
|
||||
using namespace PacketReader;
|
||||
using namespace PacketReader::IP;
|
||||
using namespace PacketReader::IP::UDP;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
const std::chrono::duration<std::chrono::steady_clock::rep, std::chrono::steady_clock::period>
|
||||
UDP_Session::MAX_IDLE = 120s; //See RFC 4787 Section 4.3
|
||||
|
||||
//TODO, figure out handling of multicast
|
||||
|
||||
UDP_Session::UDP_Session(ConnectionKey parKey, IP_Address parAdapterIP)
|
||||
: UDP_BaseSession(parKey, parAdapterIP)
|
||||
, isBroadcast(false)
|
||||
, isFixedPort(false)
|
||||
, deathClockStart(std::chrono::steady_clock::now())
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
UDP_Session::UDP_Session(ConnectionKey parKey, IP_Address parAdapterIP, bool parIsBroadcast, bool parIsMulticast, SOCKET parClient)
|
||||
#elif defined(__POSIX__)
|
||||
UDP_Session::UDP_Session(ConnectionKey parKey, IP_Address parAdapterIP, bool parIsBroadcast, bool parIsMulticast, int parClient)
|
||||
#endif
|
||||
: UDP_BaseSession(parKey, parAdapterIP)
|
||||
, open(true)
|
||||
, client(parClient)
|
||||
, srcPort(parKey.ps2Port)
|
||||
, destPort(parKey.srvPort)
|
||||
, isBroadcast(parIsBroadcast)
|
||||
, isMulticast(parIsMulticast)
|
||||
, isFixedPort(true)
|
||||
, deathClockStart(std::chrono::steady_clock::now())
|
||||
{
|
||||
}
|
||||
|
||||
IP_Payload* UDP_Session::Recv()
|
||||
{
|
||||
if (!open)
|
||||
return nullptr;
|
||||
|
||||
if (isFixedPort)
|
||||
{
|
||||
if (std::chrono::steady_clock::now() - deathClockStart.load() > MAX_IDLE)
|
||||
{
|
||||
CloseSocket();
|
||||
Console.WriteLn("DEV9: UDP: UDPFixed Max Idle Reached");
|
||||
RaiseEventConnectionClosed();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ret;
|
||||
fd_set sReady;
|
||||
fd_set sExcept;
|
||||
|
||||
timeval nowait{0};
|
||||
FD_ZERO(&sReady);
|
||||
FD_ZERO(&sExcept);
|
||||
FD_SET(client, &sReady);
|
||||
FD_SET(client, &sExcept);
|
||||
ret = select(client + 1, &sReady, nullptr, &sExcept, &nowait);
|
||||
|
||||
bool hasData;
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
hasData = false;
|
||||
Console.Error("DEV9: UDP: Select Failed. Error Code: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
}
|
||||
else if (FD_ISSET(client, &sExcept))
|
||||
{
|
||||
hasData = false;
|
||||
|
||||
int error = 0;
|
||||
#ifdef _WIN32
|
||||
int len = sizeof(error);
|
||||
if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
|
||||
Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(client, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
|
||||
Console.Error("DEV9: UDP: Unkown UDP Connection Error (getsockopt Error: %d)", errno);
|
||||
#endif
|
||||
else
|
||||
Console.Error("DEV9: UDP: Recv Error: %d", error);
|
||||
}
|
||||
else
|
||||
hasData = FD_ISSET(client, &sReady);
|
||||
|
||||
if (hasData)
|
||||
{
|
||||
u_long available = 0;
|
||||
PayloadData* recived = nullptr;
|
||||
std::unique_ptr<u8[]> buffer;
|
||||
sockaddr endpoint{0};
|
||||
|
||||
//FIONREAD returns total size of all available messages
|
||||
//but we will read one message at a time
|
||||
#ifdef _WIN32
|
||||
ret = ioctlsocket(client, FIONREAD, &available);
|
||||
#elif defined(__POSIX__)
|
||||
ret = ioctl(client, FIONREAD, &available);
|
||||
#endif
|
||||
if (ret != SOCKET_ERROR)
|
||||
{
|
||||
buffer = std::make_unique<u8[]>(available);
|
||||
|
||||
#ifdef _WIN32
|
||||
int fromlen = sizeof(endpoint);
|
||||
#elif defined(__POSIX__)
|
||||
socklen_t fromlen = sizeof(endpoint);
|
||||
#endif
|
||||
ret = recvfrom(client, (char*)buffer.get(), available, 0, &endpoint, &fromlen);
|
||||
}
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
Console.Error("DEV9: UDP: Recv Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
RaiseEventConnectionClosed();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
recived = new PayloadData(ret);
|
||||
memcpy(recived->data.get(), buffer.get(), ret);
|
||||
|
||||
UDP_Packet* iRet = new UDP_Packet(recived);
|
||||
iRet->destinationPort = srcPort;
|
||||
iRet->sourcePort = destPort;
|
||||
|
||||
deathClockStart.store(std::chrono::steady_clock::now());
|
||||
|
||||
return iRet;
|
||||
}
|
||||
|
||||
if (std::chrono::steady_clock::now() - deathClockStart.load() > MAX_IDLE)
|
||||
{
|
||||
//CloseSocket();
|
||||
Console.WriteLn("DEV9: UDP: Max Idle Reached");
|
||||
RaiseEventConnectionClosed();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UDP_Session::WillRecive(IP_Address parDestIP)
|
||||
{
|
||||
if (!open)
|
||||
return false;
|
||||
|
||||
if (isBroadcast || (parDestIP == destIP))
|
||||
{
|
||||
deathClockStart.store(std::chrono::steady_clock::now());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UDP_Session::Send(PacketReader::IP::IP_Payload* payload)
|
||||
{
|
||||
deathClockStart.store(std::chrono::steady_clock::now());
|
||||
|
||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(payload);
|
||||
UDP_Packet udp(ipPayload->data, ipPayload->GetLength());
|
||||
|
||||
if (destPort != 0)
|
||||
{
|
||||
//client already created
|
||||
if (!(udp.destinationPort == destPort && udp.sourcePort == srcPort))
|
||||
{
|
||||
Console.Error("DEV9: UDP: Packet invalid for current session (Duplicate key?)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//create client
|
||||
destPort = udp.destinationPort;
|
||||
srcPort = udp.sourcePort;
|
||||
|
||||
//Multicast address start with 0b1110
|
||||
if ((destIP.bytes[0] & 0xF0) == 0xE0)
|
||||
{
|
||||
isMulticast = true;
|
||||
Console.Error("DEV9: UDP: Unexpected Multicast Connection");
|
||||
}
|
||||
|
||||
int ret;
|
||||
client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (client == INVALID_SOCKET)
|
||||
{
|
||||
Console.Error("DEV9: UDP: Failed to open socket. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
RaiseEventConnectionClosed();
|
||||
return false;
|
||||
}
|
||||
|
||||
const int reuseAddress = true; //BOOL
|
||||
ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddress, sizeof(reuseAddress));
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
Console.Error("DEV9: UDP: Failed to set SO_REUSEADDR. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
|
||||
if (adapterIP.integer != 0)
|
||||
{
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr = adapterIP;
|
||||
|
||||
ret = bind(client, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
Console.Error("DEV9: UDP: Failed to bind socket. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
pxAssert(isMulticast == false);
|
||||
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr = destIP;
|
||||
endpoint.sin_port = htons(destPort);
|
||||
|
||||
ret = connect(client, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
Console.Error("DEV9: UDP: Failed to connect socket. Error: %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
RaiseEventConnectionClosed();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (srcPort != 0)
|
||||
open = true;
|
||||
}
|
||||
|
||||
PayloadPtr* udpPayload = static_cast<PayloadPtr*>(udp.GetPayload());
|
||||
|
||||
//Send
|
||||
int ret = SOCKET_ERROR;
|
||||
if (isBroadcast)
|
||||
{
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
endpoint.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
endpoint.sin_port = htons(destPort);
|
||||
|
||||
ret = sendto(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
}
|
||||
else if (isMulticast | isFixedPort)
|
||||
{
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr = destIP;
|
||||
endpoint.sin_port = htons(destPort);
|
||||
|
||||
ret = sendto(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
}
|
||||
else
|
||||
ret = send(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0);
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ret = WSAGetLastError();
|
||||
#elif defined(__POSIX__)
|
||||
ret = errno;
|
||||
#endif
|
||||
Console.Error("DEV9: UDP: Send Error %d", ret);
|
||||
|
||||
//We can recive an ICMP Port Unreacable error, which can get raised in send (and maybe sendto?)
|
||||
//On Windows this an WSAECONNRESET error, although I've not been able to reproduce in testing
|
||||
//On Linux this is an ECONNREFUSED error (Testing needed to confirm full behaviour)
|
||||
|
||||
//The decision to ignore the error and retry was made to allow R&C Deadlock ressurection team to packet capture eveything
|
||||
#ifdef _WIN32
|
||||
if (ret == WSAECONNRESET)
|
||||
#elif defined(__POSIX__)
|
||||
if (ret == ECONNREFUSED)
|
||||
#endif
|
||||
{
|
||||
pxAssert(isBroadcast == false && isMulticast == false);
|
||||
if (isFixedPort)
|
||||
{
|
||||
sockaddr_in endpoint{0};
|
||||
endpoint.sin_family = AF_INET;
|
||||
*(IP_Address*)&endpoint.sin_addr = destIP;
|
||||
endpoint.sin_port = htons(destPort);
|
||||
|
||||
ret = sendto(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0, (const sockaddr*)&endpoint, sizeof(endpoint));
|
||||
}
|
||||
else
|
||||
//Do we need to clear the error somehow?
|
||||
ret = send(client, (const char*)udpPayload->data, udpPayload->GetLength(), 0);
|
||||
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
Console.Error("DEV9: UDP: Send Error (Second attempt) %d",
|
||||
#ifdef _WIN32
|
||||
WSAGetLastError());
|
||||
#elif defined(__POSIX__)
|
||||
errno);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseEventConnectionClosed();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pxAssert(ret == udpPayload->GetLength());
|
||||
|
||||
if (srcPort == 0)
|
||||
RaiseEventConnectionClosed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UDP_Session::CloseSocket()
|
||||
{
|
||||
open = false;
|
||||
if (!isFixedPort && client != INVALID_SOCKET)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
closesocket(client);
|
||||
#elif defined(__POSIX__)
|
||||
::close(client);
|
||||
#endif
|
||||
client = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
void UDP_Session::Reset()
|
||||
{
|
||||
//CloseSocket();
|
||||
RaiseEventConnectionClosed();
|
||||
}
|
||||
|
||||
UDP_Session::~UDP_Session()
|
||||
{
|
||||
CloseSocket();
|
||||
}
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,72 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#elif defined(__POSIX__)
|
||||
#define INVALID_SOCKET -1
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include "UDP_BaseSession.h"
|
||||
|
||||
namespace Sessions
|
||||
{
|
||||
class UDP_Session : public UDP_BaseSession
|
||||
{
|
||||
private:
|
||||
std::atomic<bool> open{false};
|
||||
|
||||
#ifdef _WIN32
|
||||
SOCKET client = INVALID_SOCKET;
|
||||
#elif defined(__POSIX__)
|
||||
int client = INVALID_SOCKET;
|
||||
#endif
|
||||
|
||||
u16 srcPort = 0;
|
||||
u16 destPort = 0;
|
||||
//Broadcast
|
||||
const bool isBroadcast; // = false;
|
||||
bool isMulticast = false;
|
||||
const bool isFixedPort; // = false;
|
||||
//EndBroadcast
|
||||
|
||||
std::atomic<std::chrono::steady_clock::time_point> deathClockStart;
|
||||
const static std::chrono::duration<std::chrono::steady_clock::rep, std::chrono::steady_clock::period> MAX_IDLE;
|
||||
|
||||
public:
|
||||
//Normal Port
|
||||
UDP_Session(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP);
|
||||
//Fixed Port
|
||||
#ifdef _WIN32
|
||||
UDP_Session(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP, bool parIsBroadcast, bool parIsMulticast, SOCKET parClient);
|
||||
#elif defined(__POSIX__)
|
||||
UDP_Session(ConnectionKey parKey, PacketReader::IP::IP_Address parAdapterIP, bool parIsBroadcast, bool parIsMulticast, int parClient);
|
||||
#endif
|
||||
|
||||
virtual PacketReader::IP::IP_Payload* Recv();
|
||||
virtual bool WillRecive(PacketReader::IP::IP_Address parDestIP);
|
||||
virtual bool Send(PacketReader::IP::IP_Payload* payload);
|
||||
virtual void Reset();
|
||||
|
||||
virtual ~UDP_Session();
|
||||
|
||||
private:
|
||||
void CloseSocket();
|
||||
};
|
||||
} // namespace Sessions
|
|
@ -0,0 +1,112 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_OSX == 1
|
||||
#include <Availability.h>
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
|
||||
#define NO_SHARED_MUTEX
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template <class Key, class T>
|
||||
class ThreadSafeMap
|
||||
{
|
||||
#ifdef NO_SHARED_MUTEX
|
||||
std::mutex accessMutex;
|
||||
#else
|
||||
std::shared_mutex accessMutex;
|
||||
#endif
|
||||
|
||||
std::unordered_map<Key, T> map;
|
||||
|
||||
public:
|
||||
void Add(Key key, T value)
|
||||
{
|
||||
std::unique_lock modifyLock(accessMutex);
|
||||
//Todo, check if key already exists?
|
||||
map[key] = value;
|
||||
}
|
||||
|
||||
void Remove(Key key)
|
||||
{
|
||||
std::unique_lock modifyLock(accessMutex);
|
||||
map.erase(key);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
std::unique_lock modifyLock(accessMutex);
|
||||
map.clear();
|
||||
}
|
||||
|
||||
std::vector<Key> GetKeys()
|
||||
{
|
||||
#ifdef NO_SHARED_MUTEX
|
||||
std::unique_lock readLock(accessMutex);
|
||||
#else
|
||||
std::shared_lock readLock(accessMutex);
|
||||
#endif
|
||||
|
||||
std::vector<Key> keys;
|
||||
keys.reserve(map.size());
|
||||
|
||||
for (auto iter = map.begin(); iter != map.end(); ++iter)
|
||||
keys.push_back(iter->first);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
//Does not error or insert if no key is found
|
||||
bool TryGetValue(Key key, T* value)
|
||||
{
|
||||
#ifdef NO_SHARED_MUTEX
|
||||
std::unique_lock readLock(accessMutex);
|
||||
#else
|
||||
std::shared_lock readLock(accessMutex);
|
||||
#endif
|
||||
auto search = map.find(key);
|
||||
if (search != map.end())
|
||||
{
|
||||
*value = map[key];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContainsKey(Key key)
|
||||
{
|
||||
#ifdef NO_SHARED_MUTEX
|
||||
std::unique_lock readLock(accessMutex);
|
||||
#else
|
||||
std::shared_lock readLock(accessMutex);
|
||||
#endif
|
||||
auto search = map.find(key);
|
||||
if (search != map.end())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
|
@ -28,6 +28,7 @@
|
|||
#include "Win32/tap.h"
|
||||
#endif
|
||||
#include "pcap_io.h"
|
||||
#include "sockets.h"
|
||||
|
||||
#include "PacketReader/EthernetFrame.h"
|
||||
#include "PacketReader/IP/IP_Packet.h"
|
||||
|
@ -88,6 +89,9 @@ NetAdapter* GetNetAdapter()
|
|||
case Pcsx2Config::DEV9Options::NetApi::PCAP_Switched:
|
||||
na = static_cast<NetAdapter*>(new PCAPAdapter());
|
||||
break;
|
||||
case Pcsx2Config::DEV9Options::NetApi::Sockets:
|
||||
na = static_cast<NetAdapter*>(new SocketAdapter());
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,932 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#ifdef __POSIX__
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "sockets.h"
|
||||
#include "DEV9.h"
|
||||
|
||||
#include "Sessions/ICMP_Session/ICMP_Session.h"
|
||||
#include "Sessions/TCP_Session/TCP_Session.h"
|
||||
#include "Sessions/UDP_Session/UDP_FixedPort.h"
|
||||
#include "Sessions/UDP_Session/UDP_Session.h"
|
||||
|
||||
#include "PacketReader/EthernetFrame.h"
|
||||
#include "PacketReader/ARP/ARP_Packet.h"
|
||||
#include "PacketReader/IP/ICMP/ICMP_Packet.h"
|
||||
#include "PacketReader/IP/TCP/TCP_Packet.h"
|
||||
#include "PacketReader/IP/UDP/UDP_Packet.h"
|
||||
|
||||
using namespace Sessions;
|
||||
using namespace PacketReader;
|
||||
using namespace PacketReader::ARP;
|
||||
using namespace PacketReader::IP;
|
||||
using namespace PacketReader::IP::ICMP;
|
||||
using namespace PacketReader::IP::TCP;
|
||||
using namespace PacketReader::IP::UDP;
|
||||
|
||||
std::vector<AdapterEntry> SocketAdapter::GetAdapters()
|
||||
{
|
||||
std::vector<AdapterEntry> nic;
|
||||
AdapterEntry autoEntry;
|
||||
autoEntry.type = Pcsx2Config::DEV9Options::NetApi::Sockets;
|
||||
autoEntry.name = "Auto";
|
||||
autoEntry.guid = "Auto";
|
||||
nic.push_back(autoEntry);
|
||||
|
||||
#ifdef _WIN32
|
||||
int neededSize = 128;
|
||||
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
|
||||
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
|
||||
|
||||
PIP_ADAPTER_ADDRESSES pAdapterInfo;
|
||||
|
||||
DWORD dwStatus = GetAdaptersAddresses(
|
||||
AF_UNSPEC,
|
||||
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
|
||||
NULL,
|
||||
AdapterInfo.get(),
|
||||
&dwBufLen);
|
||||
|
||||
if (dwStatus == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
DevCon.WriteLn("DEV9: PCAPGetWin32Adapter() buffer too small, resizing");
|
||||
//
|
||||
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
|
||||
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
|
||||
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
|
||||
DevCon.WriteLn("DEV9: New size %i", neededSize);
|
||||
|
||||
dwStatus = GetAdaptersAddresses(
|
||||
AF_UNSPEC,
|
||||
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
|
||||
NULL,
|
||||
AdapterInfo.get(),
|
||||
&dwBufLen);
|
||||
}
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS)
|
||||
return nic;
|
||||
|
||||
pAdapterInfo = AdapterInfo.get();
|
||||
|
||||
do
|
||||
{
|
||||
if (pAdapterInfo->IfType != IF_TYPE_SOFTWARE_LOOPBACK &&
|
||||
pAdapterInfo->OperStatus == IfOperStatusUp)
|
||||
{
|
||||
AdapterEntry entry;
|
||||
entry.type = Pcsx2Config::DEV9Options::NetApi::Sockets;
|
||||
entry.name = StringUtil::WideStringToUTF8String(pAdapterInfo->FriendlyName);
|
||||
entry.guid = pAdapterInfo->AdapterName;
|
||||
|
||||
nic.push_back(entry);
|
||||
}
|
||||
|
||||
pAdapterInfo = pAdapterInfo->Next;
|
||||
} while (pAdapterInfo);
|
||||
|
||||
#elif defined(__POSIX__)
|
||||
ifaddrs* adapterInfo;
|
||||
ifaddrs* pAdapter;
|
||||
|
||||
int error = getifaddrs(&adapterInfo);
|
||||
if (error)
|
||||
return nic;
|
||||
|
||||
pAdapter = adapterInfo;
|
||||
|
||||
do
|
||||
{
|
||||
if ((pAdapter->ifa_flags & IFF_LOOPBACK) == 0 &&
|
||||
(pAdapter->ifa_flags & IFF_UP) != 0 &&
|
||||
pAdapter->ifa_addr != nullptr &&
|
||||
pAdapter->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
AdapterEntry entry;
|
||||
entry.type = Pcsx2Config::DEV9Options::NetApi::Sockets;
|
||||
entry.name = pAdapter->ifa_name;
|
||||
entry.guid = pAdapter->ifa_name;
|
||||
|
||||
nic.push_back(entry);
|
||||
}
|
||||
|
||||
pAdapter = pAdapter->ifa_next;
|
||||
} while (pAdapter);
|
||||
|
||||
freeifaddrs(adapterInfo);
|
||||
#endif
|
||||
|
||||
return nic;
|
||||
}
|
||||
|
||||
AdapterOptions SocketAdapter::GetAdapterOptions()
|
||||
{
|
||||
return (AdapterOptions::DHCP_ForcedOn | AdapterOptions::DHCP_OverrideIP | AdapterOptions::DHCP_OverideSubnet | AdapterOptions::DHCP_OverideGateway);
|
||||
}
|
||||
|
||||
SocketAdapter::SocketAdapter()
|
||||
{
|
||||
bool foundAdapter;
|
||||
|
||||
#ifdef _WIN32
|
||||
IP_ADAPTER_ADDRESSES adapter;
|
||||
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
|
||||
|
||||
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
|
||||
{
|
||||
foundAdapter = GetWin32SelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
|
||||
|
||||
if (!foundAdapter)
|
||||
{
|
||||
Console.Error("DEV9: Socket: Failed to Get Adapter");
|
||||
return;
|
||||
}
|
||||
|
||||
PIP_ADAPTER_UNICAST_ADDRESS address = nullptr;
|
||||
|
||||
address = adapter.FirstUnicastAddress;
|
||||
while (address != nullptr && address->Address.lpSockaddr->sa_family != AF_INET)
|
||||
address = address->Next;
|
||||
|
||||
if (address != nullptr)
|
||||
{
|
||||
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
|
||||
adapterIP = *(IP_Address*)&sockaddr->sin_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("DEV9: Socket: Failed To Get Adapter IP");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foundAdapter = GetWin32AutoAdapter(&adapter, &buffer);
|
||||
adapterIP = {0};
|
||||
|
||||
if (!foundAdapter)
|
||||
{
|
||||
Console.Error("DEV9: Socket: Auto Selection Failed, Check You Connection or Manually Specify Adapter");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#elif defined(__POSIX__)
|
||||
ifaddrs adapter;
|
||||
ifaddrs* buffer;
|
||||
|
||||
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
|
||||
{
|
||||
foundAdapter = GetIfSelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
|
||||
|
||||
if (!foundAdapter)
|
||||
{
|
||||
Console.Error("DEV9: Socket: Failed to Get Adapter");
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr* address = nullptr;
|
||||
|
||||
if (adapter.ifa_addr != nullptr && adapter.ifa_addr->sa_family == AF_INET)
|
||||
address = adapter.ifa_addr;
|
||||
|
||||
if (address != nullptr)
|
||||
{
|
||||
sockaddr_in* sockaddr = (sockaddr_in*)address;
|
||||
adapterIP = *(IP_Address*)&sockaddr->sin_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("DEV9: Socket: Failed To Get Adapter IP");
|
||||
freeifaddrs(buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foundAdapter = GetIfAutoAdapter(&adapter, &buffer);
|
||||
adapterIP = {0};
|
||||
|
||||
if (!foundAdapter)
|
||||
{
|
||||
Console.Error("DEV9: Socket: Auto Selection Failed, Check You Connection or Manually Specify Adapter");
|
||||
freeifaddrs(buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//For DHCP, we need to override some settings
|
||||
//DNS settings as per direct adapters
|
||||
|
||||
const IP_Address ps2IP{internalIP.bytes[0], internalIP.bytes[1], internalIP.bytes[2], 100};
|
||||
const IP_Address subnet{255, 255, 255, 0};
|
||||
const IP_Address gateway = internalIP;
|
||||
|
||||
InitInternalServer(&adapter, true, ps2IP, subnet, gateway);
|
||||
#ifdef __POSIX__
|
||||
freeifaddrs(buffer);
|
||||
#endif
|
||||
|
||||
u8 hostMAC[6];
|
||||
u8 newMAC[6];
|
||||
|
||||
#ifdef _WIN32
|
||||
memcpy(hostMAC, adapter.PhysicalAddress, 6);
|
||||
#elif defined(__linux__)
|
||||
struct ifreq ifr;
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
strcpy(ifr.ifr_name, adapter.ifa_name);
|
||||
if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr))
|
||||
memcpy(hostMAC, ifr.ifr_hwaddr.sa_data, 6);
|
||||
else
|
||||
{
|
||||
memcpy(hostMAC, ps2MAC, 6);
|
||||
Console.Error("Could not get MAC address for adapter: %s", adapter.ifa_name);
|
||||
}
|
||||
::close(fd);
|
||||
#else
|
||||
memcpy(hostMAC, ps2MAC, 6);
|
||||
Console.Error("Could not get MAC address for adapter, OS not supported");
|
||||
#endif
|
||||
memcpy(newMAC, ps2MAC, 6);
|
||||
|
||||
//Lets take the hosts last 2 bytes to make it unique on Xlink
|
||||
newMAC[5] = hostMAC[4];
|
||||
newMAC[4] = hostMAC[5];
|
||||
|
||||
SetMACAddress(newMAC);
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
|
||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||
|
||||
WSADATA wsaData{0};
|
||||
const int err = WSAStartup(wVersionRequested, &wsaData);
|
||||
if (err != 0)
|
||||
{
|
||||
Console.Error("DEV9: WSAStartup failed with error: %d\n", err);
|
||||
return;
|
||||
}
|
||||
else
|
||||
wsa_init = true;
|
||||
#endif
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool SocketAdapter::GetWin32SelectedAdapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
|
||||
{
|
||||
int neededSize = 128;
|
||||
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
|
||||
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
|
||||
|
||||
PIP_ADAPTER_ADDRESSES pAdapterInfo;
|
||||
|
||||
DWORD dwStatus = GetAdaptersAddresses(
|
||||
AF_UNSPEC,
|
||||
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
|
||||
NULL,
|
||||
AdapterInfo.get(),
|
||||
&dwBufLen);
|
||||
|
||||
if (dwStatus == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
DevCon.WriteLn("DEV9: GetWin32Adapter() buffer too small, resizing");
|
||||
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
|
||||
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
|
||||
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
|
||||
DevCon.WriteLn("DEV9: New size %i", neededSize);
|
||||
|
||||
dwStatus = GetAdaptersAddresses(
|
||||
AF_UNSPEC,
|
||||
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
|
||||
NULL,
|
||||
AdapterInfo.get(),
|
||||
&dwBufLen);
|
||||
}
|
||||
if (dwStatus != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
pAdapterInfo = AdapterInfo.get();
|
||||
|
||||
do
|
||||
{
|
||||
if (strcmp(pAdapterInfo->AdapterName, name.c_str()) == 0)
|
||||
{
|
||||
*adapter = *pAdapterInfo;
|
||||
buffer->swap(AdapterInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
pAdapterInfo = pAdapterInfo->Next;
|
||||
} while (pAdapterInfo);
|
||||
return false;
|
||||
}
|
||||
bool SocketAdapter::GetWin32AutoAdapter(PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
|
||||
{
|
||||
int neededSize = 128;
|
||||
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
|
||||
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
|
||||
|
||||
PIP_ADAPTER_ADDRESSES pAdapterInfo;
|
||||
|
||||
DWORD dwStatus = GetAdaptersAddresses(
|
||||
AF_UNSPEC,
|
||||
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
|
||||
NULL,
|
||||
AdapterInfo.get(),
|
||||
&dwBufLen);
|
||||
|
||||
if (dwStatus == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
DevCon.WriteLn("DEV9: PCAPGetWin32Adapter() buffer too small, resizing");
|
||||
//
|
||||
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
|
||||
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
|
||||
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
|
||||
DevCon.WriteLn("DEV9: New size %i", neededSize);
|
||||
|
||||
dwStatus = GetAdaptersAddresses(
|
||||
AF_UNSPEC,
|
||||
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
|
||||
NULL,
|
||||
AdapterInfo.get(),
|
||||
&dwBufLen);
|
||||
}
|
||||
|
||||
if (dwStatus != ERROR_SUCCESS)
|
||||
return 0;
|
||||
|
||||
pAdapterInfo = AdapterInfo.get();
|
||||
|
||||
do
|
||||
{
|
||||
if (pAdapterInfo->IfType != IF_TYPE_SOFTWARE_LOOPBACK &&
|
||||
pAdapterInfo->OperStatus == IfOperStatusUp)
|
||||
{
|
||||
//Search for an adapter with;
|
||||
//IPv4 Address
|
||||
//DNS
|
||||
//Gateway
|
||||
|
||||
bool hasIPv4 = false;
|
||||
bool hasDNS = false;
|
||||
bool hasGateway = false;
|
||||
|
||||
//IPv4
|
||||
PIP_ADAPTER_UNICAST_ADDRESS ipAddress = nullptr;
|
||||
ipAddress = pAdapterInfo->FirstUnicastAddress;
|
||||
while (ipAddress != nullptr && ipAddress->Address.lpSockaddr->sa_family != AF_INET)
|
||||
ipAddress = ipAddress->Next;
|
||||
if (ipAddress != nullptr)
|
||||
hasIPv4 = true;
|
||||
|
||||
//DNS
|
||||
PIP_ADAPTER_DNS_SERVER_ADDRESS dnsAddress = pAdapterInfo->FirstDnsServerAddress;
|
||||
while (dnsAddress != nullptr && dnsAddress->Address.lpSockaddr->sa_family != AF_INET)
|
||||
dnsAddress = dnsAddress->Next;
|
||||
if (dnsAddress != nullptr)
|
||||
hasDNS = true;
|
||||
|
||||
//Gateway
|
||||
PIP_ADAPTER_GATEWAY_ADDRESS gatewayAddress = pAdapterInfo->FirstGatewayAddress;
|
||||
while (gatewayAddress != nullptr && gatewayAddress->Address.lpSockaddr->sa_family != AF_INET)
|
||||
gatewayAddress = gatewayAddress->Next;
|
||||
if (gatewayAddress != nullptr)
|
||||
hasGateway = true;
|
||||
|
||||
if (hasIPv4 && hasDNS && hasGateway)
|
||||
{
|
||||
*adapter = *pAdapterInfo;
|
||||
buffer->swap(AdapterInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pAdapterInfo = pAdapterInfo->Next;
|
||||
} while (pAdapterInfo);
|
||||
|
||||
return false;
|
||||
}
|
||||
#elif defined(__POSIX__)
|
||||
bool SocketAdapter::GetIfSelectedAdapter(const std::string& name, ifaddrs* adapter, ifaddrs** buffer)
|
||||
{
|
||||
ifaddrs* adapterInfo;
|
||||
ifaddrs* pAdapter;
|
||||
|
||||
int error = getifaddrs(&adapterInfo);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
pAdapter = adapterInfo;
|
||||
|
||||
do
|
||||
{
|
||||
if (pAdapter->ifa_addr->sa_family == AF_INET && strcmp(pAdapter->ifa_name, name.c_str()) == 0)
|
||||
break;
|
||||
|
||||
pAdapter = pAdapter->ifa_next;
|
||||
} while (pAdapter);
|
||||
|
||||
if (pAdapter != nullptr)
|
||||
{
|
||||
*adapter = *pAdapter;
|
||||
*buffer = adapterInfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
freeifaddrs(adapterInfo);
|
||||
return false;
|
||||
}
|
||||
bool SocketAdapter::GetIfAutoAdapter(ifaddrs* adapter, ifaddrs** buffer)
|
||||
{
|
||||
ifaddrs* adapterInfo;
|
||||
ifaddrs* pAdapter;
|
||||
|
||||
int error = getifaddrs(&adapterInfo);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
pAdapter = adapterInfo;
|
||||
|
||||
do
|
||||
{
|
||||
if ((pAdapter->ifa_flags & IFF_LOOPBACK) == 0 &&
|
||||
(pAdapter->ifa_flags & IFF_UP) != 0)
|
||||
{
|
||||
//Search for an adapter with;
|
||||
//IPv4 Address
|
||||
//Gateway
|
||||
|
||||
bool hasIPv4 = false;
|
||||
bool hasGateway = false;
|
||||
|
||||
if (pAdapter->ifa_addr->sa_family == AF_INET)
|
||||
hasIPv4 = true;
|
||||
|
||||
#ifdef __linux__
|
||||
std::vector<IP_Address> gateways = InternalServers::DHCP_Server::GetGatewaysLinux(pAdapter->ifa_name);
|
||||
|
||||
if (gateways.size() > 0)
|
||||
hasGateway = true;
|
||||
#else
|
||||
Console.Error("DHCP: Unsupported OS, can't find Gateway");
|
||||
#endif
|
||||
if (hasIPv4 && hasGateway)
|
||||
{
|
||||
*adapter = *pAdapter;
|
||||
*buffer = adapterInfo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pAdapter = pAdapter->ifa_next;
|
||||
} while (pAdapter);
|
||||
|
||||
freeifaddrs(adapterInfo);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SocketAdapter::blocks()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SocketAdapter::isInitialised()
|
||||
{
|
||||
return initialized;
|
||||
}
|
||||
|
||||
bool SocketAdapter::recv(NetPacket* pkt)
|
||||
{
|
||||
if (NetAdapter::recv(pkt))
|
||||
return true;
|
||||
|
||||
EthernetFrame* bFrame;
|
||||
if (!vRecBuffer.Dequeue(&bFrame))
|
||||
{
|
||||
std::vector<ConnectionKey> keys = connections.GetKeys();
|
||||
for (size_t i = 0; i < keys.size(); i++)
|
||||
{
|
||||
const ConnectionKey key = keys[i];
|
||||
|
||||
BaseSession* session;
|
||||
if (!connections.TryGetValue(key, &session))
|
||||
continue;
|
||||
|
||||
IP_Payload* pl = session->Recv();
|
||||
|
||||
if (pl != nullptr)
|
||||
{
|
||||
IP_Packet* ipPkt = new IP_Packet(pl);
|
||||
ipPkt->destinationIP = session->sourceIP;
|
||||
ipPkt->sourceIP = session->destIP;
|
||||
|
||||
EthernetFrame frame(ipPkt);
|
||||
memcpy(frame.sourceMAC, internalMAC, 6);
|
||||
memcpy(frame.destinationMAC, ps2MAC, 6);
|
||||
frame.protocol = (u16)EtherType::IPv4;
|
||||
|
||||
frame.WritePacket(pkt);
|
||||
InspectRecv(pkt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bFrame->WritePacket(pkt);
|
||||
InspectRecv(pkt);
|
||||
|
||||
delete bFrame;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SocketAdapter::send(NetPacket* pkt)
|
||||
{
|
||||
InspectSend(pkt);
|
||||
if (NetAdapter::send(pkt))
|
||||
return true;
|
||||
|
||||
bool result = false;
|
||||
|
||||
EthernetFrame frame(pkt);
|
||||
|
||||
switch (frame.protocol)
|
||||
{
|
||||
case (u16)EtherType::null:
|
||||
case 0x0C00:
|
||||
//Packets with the above ethertypes get sent when the adapter is reset
|
||||
//Catch them here instead of printing an error
|
||||
return true;
|
||||
case (int)EtherType::IPv4:
|
||||
{
|
||||
PayloadPtr* payload = static_cast<PayloadPtr*>(frame.GetPayload());
|
||||
IP_Packet ippkt(payload->data, payload->GetLength());
|
||||
|
||||
return SendIP(&ippkt);
|
||||
}
|
||||
case (u16)EtherType::ARP:
|
||||
{
|
||||
PayloadPtr* payload = static_cast<PayloadPtr*>(frame.GetPayload());
|
||||
ARP_Packet arpPkt(payload->data, payload->GetLength());
|
||||
|
||||
if (arpPkt.protocol == (u16)EtherType::IPv4)
|
||||
{
|
||||
if (arpPkt.op == 1) //ARP request
|
||||
{
|
||||
if (*(IP_Address*)arpPkt.targetProtocolAddress.get() != dhcpServer.ps2IP)
|
||||
//it's trying to resolve the virtual gateway's mac addr
|
||||
{
|
||||
ARP_Packet* arpRet = new ARP_Packet(6, 4);
|
||||
memcpy(arpRet->targetHardwareAddress.get(), arpPkt.senderHardwareAddress.get(), 6);
|
||||
memcpy(arpRet->senderHardwareAddress.get(), internalMAC, 6);
|
||||
memcpy(arpRet->targetProtocolAddress.get(), arpPkt.senderProtocolAddress.get(), 4);
|
||||
memcpy(arpRet->senderProtocolAddress.get(), arpPkt.targetProtocolAddress.get(), 4);
|
||||
arpRet->op = 2,
|
||||
arpRet->protocol = arpPkt.protocol;
|
||||
|
||||
EthernetFrame* retARP = new EthernetFrame(arpRet);
|
||||
memcpy(retARP->destinationMAC, ps2MAC, 6);
|
||||
memcpy(retARP->sourceMAC, internalMAC, 6);
|
||||
retARP->protocol = (u16)EtherType::ARP;
|
||||
|
||||
vRecBuffer.Enqueue(retARP);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
Console.Error("DEV9: Socket: Unkown EtherframeType %X", frame.protocol);
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SocketAdapter::reset()
|
||||
{
|
||||
//Adapter Reset
|
||||
std::vector<ConnectionKey> keys = connections.GetKeys();
|
||||
DevCon.WriteLn("DEV9: Socket: Reset %d Connections", keys.size());
|
||||
for (size_t i = 0; i < keys.size(); i++)
|
||||
{
|
||||
ConnectionKey key = keys[i];
|
||||
|
||||
BaseSession* session;
|
||||
if (!connections.TryGetValue(key, &session))
|
||||
continue;
|
||||
|
||||
session->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SocketAdapter::reloadSettings()
|
||||
{
|
||||
bool foundAdapter = false;
|
||||
#ifdef _WIN32
|
||||
IP_ADAPTER_ADDRESSES adapter;
|
||||
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
|
||||
|
||||
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
|
||||
foundAdapter = GetWin32SelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
|
||||
else
|
||||
foundAdapter = GetWin32AutoAdapter(&adapter, &buffer);
|
||||
|
||||
#elif defined(__POSIX__)
|
||||
ifaddrs adapter;
|
||||
ifaddrs* buffer;
|
||||
|
||||
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
|
||||
foundAdapter = GetIfSelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
|
||||
else
|
||||
foundAdapter = GetIfAutoAdapter(&adapter, &buffer);
|
||||
|
||||
if (foundAdapter)
|
||||
freeifaddrs(buffer);
|
||||
#endif
|
||||
|
||||
const IP_Address ps2IP = {internalIP.bytes[0], internalIP.bytes[1], internalIP.bytes[2], 100};
|
||||
const IP_Address subnet{255, 255, 255, 0};
|
||||
const IP_Address gateway = internalIP;
|
||||
|
||||
if (foundAdapter)
|
||||
{
|
||||
ReloadInternalServer(&adapter, true, ps2IP, subnet, gateway);
|
||||
#ifdef __POSIX__
|
||||
freeifaddrs(buffer);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
pxAssert(false);
|
||||
ReloadInternalServer(nullptr, true, ps2IP, subnet, gateway);
|
||||
}
|
||||
}
|
||||
|
||||
bool SocketAdapter::SendIP(IP_Packet* ipPkt)
|
||||
{
|
||||
if (ipPkt->VerifyChecksum() == false)
|
||||
{
|
||||
Console.Error("DEV9: Socket: IP packet with bad CSUM");
|
||||
return false;
|
||||
}
|
||||
//Do Checksum in sub functions
|
||||
|
||||
ConnectionKey Key{0};
|
||||
Key.ip = ipPkt->destinationIP;
|
||||
Key.protocol = ipPkt->protocol;
|
||||
|
||||
switch (ipPkt->protocol) //(Prase Payload)
|
||||
{
|
||||
case (u8)IP_Type::ICMP:
|
||||
return SendICMP(Key, ipPkt);
|
||||
case (u8)IP_Type::IGMP:
|
||||
return SendIGMP(Key, ipPkt);
|
||||
case (u8)IP_Type::TCP:
|
||||
return SendTCP(Key, ipPkt);
|
||||
case (u8)IP_Type::UDP:
|
||||
return SendUDP(Key, ipPkt);
|
||||
default:
|
||||
//Log_Error("Unkown Protocol");
|
||||
Console.Error("DEV9: Socket: Unkown IPv4 Protocol %X", ipPkt->protocol);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SocketAdapter::SendICMP(ConnectionKey Key, IP_Packet* ipPkt)
|
||||
{
|
||||
//IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(ipPkt->GetPayload());
|
||||
|
||||
ICMP_Session* s;
|
||||
|
||||
//Need custom SendFromConnection
|
||||
BaseSession* existingSession = nullptr;
|
||||
connections.TryGetValue(Key, &existingSession);
|
||||
if (existingSession != nullptr)
|
||||
{
|
||||
s = static_cast<ICMP_Session*>(existingSession);
|
||||
return s->Send(ipPkt->GetPayload(), ipPkt);
|
||||
}
|
||||
|
||||
DevCon.WriteLn("DEV9: Socket: Creating New ICMP Connection");
|
||||
s = new ICMP_Session(Key, adapterIP, &connections);
|
||||
|
||||
s->AddConnectionClosedHandler([&](BaseSession* session) { HandleConnectionClosed(session); });
|
||||
s->destIP = ipPkt->destinationIP;
|
||||
s->sourceIP = dhcpServer.ps2IP;
|
||||
connections.Add(Key, s);
|
||||
return s->Send(ipPkt->GetPayload(), ipPkt);
|
||||
}
|
||||
|
||||
bool SocketAdapter::SendIGMP(ConnectionKey Key, IP_Packet* ipPkt)
|
||||
{
|
||||
Console.Error("DEV9: Socket: IGMP Packets not supported in socket mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SocketAdapter::SendTCP(ConnectionKey Key, IP_Packet* ipPkt)
|
||||
{
|
||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(ipPkt->GetPayload());
|
||||
TCP_Packet tcp(ipPayload->data, ipPayload->GetLength());
|
||||
|
||||
Key.ps2Port = tcp.sourcePort;
|
||||
Key.srvPort = tcp.destinationPort;
|
||||
|
||||
int res = SendFromConnection(Key, ipPkt);
|
||||
if (res == 1)
|
||||
return true;
|
||||
else if (res == 0)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
Console.WriteLn("DEV9: Socket: Creating New TCP Connection to %d", tcp.destinationPort);
|
||||
TCP_Session* s = new TCP_Session(Key, adapterIP);
|
||||
|
||||
s->AddConnectionClosedHandler([&](BaseSession* session) { HandleConnectionClosed(session); });
|
||||
s->destIP = ipPkt->destinationIP;
|
||||
s->sourceIP = dhcpServer.ps2IP;
|
||||
connections.Add(Key, s);
|
||||
return s->Send(ipPkt->GetPayload());
|
||||
}
|
||||
}
|
||||
|
||||
bool SocketAdapter::SendUDP(ConnectionKey Key, IP_Packet* ipPkt)
|
||||
{
|
||||
IP_PayloadPtr* ipPayload = static_cast<IP_PayloadPtr*>(ipPkt->GetPayload());
|
||||
UDP_Packet udp(ipPayload->data, ipPayload->GetLength());
|
||||
|
||||
Key.ps2Port = udp.sourcePort;
|
||||
Key.srvPort = udp.destinationPort;
|
||||
|
||||
const int res = SendFromConnection(Key, ipPkt);
|
||||
if (res == 1)
|
||||
return true;
|
||||
else if (res == 0)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
UDP_Session* s = nullptr;
|
||||
|
||||
if (udp.sourcePort == udp.destinationPort || //Used for LAN games that assume the destination port
|
||||
ipPkt->destinationIP == dhcpServer.broadcastIP || //Broadcast packets
|
||||
ipPkt->destinationIP == IP_Address{255, 255, 255, 255} || //Limited Broadcast packets
|
||||
(ipPkt->destinationIP.bytes[0] & 0xF0) == 0xE0) //Multicast address start with 0b1110
|
||||
{
|
||||
UDP_FixedPort* fPort = nullptr;
|
||||
BaseSession* fSession;
|
||||
if (fixedUDPPorts.TryGetValue(udp.sourcePort, &fSession))
|
||||
{
|
||||
//DevCon.WriteLn("DEV9: Socket: Using Existing UDPFixedPort");
|
||||
fPort = static_cast<UDP_FixedPort*>(fSession);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectionKey fKey{0};
|
||||
fKey.protocol = (u8)IP_Type::UDP;
|
||||
fKey.ps2Port = udp.sourcePort;
|
||||
fKey.srvPort = 0;
|
||||
|
||||
Console.WriteLn("DEV9: Socket: Creating New UDPFixedPort with port %d", udp.sourcePort);
|
||||
|
||||
fPort = new UDP_FixedPort(fKey, adapterIP, udp.sourcePort);
|
||||
fPort->AddConnectionClosedHandler([&](BaseSession* session) { HandleFixedPortClosed(session); });
|
||||
|
||||
fPort->destIP = {0, 0, 0, 0};
|
||||
fPort->sourceIP = dhcpServer.ps2IP;
|
||||
|
||||
connections.Add(fKey, fPort);
|
||||
fixedUDPPorts.Add(udp.sourcePort, fPort);
|
||||
}
|
||||
|
||||
Console.WriteLn("DEV9: Socket: Creating New UDP Connection from FixedPort %d", udp.sourcePort);
|
||||
s = fPort->NewClientSession(Key,
|
||||
ipPkt->destinationIP == dhcpServer.broadcastIP || ipPkt->destinationIP == IP_Address{255, 255, 255, 255},
|
||||
(ipPkt->destinationIP.bytes[0] & 0xF0) == 0xE0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("DEV9: Socket: Creating New UDP Connection to %d", udp.sourcePort);
|
||||
s = new UDP_Session(Key, adapterIP);
|
||||
}
|
||||
|
||||
s->AddConnectionClosedHandler([&](BaseSession* session) { HandleConnectionClosed(session); });
|
||||
s->destIP = ipPkt->destinationIP;
|
||||
s->sourceIP = dhcpServer.ps2IP;
|
||||
connections.Add(Key, s);
|
||||
return s->Send(ipPkt->GetPayload());
|
||||
}
|
||||
}
|
||||
|
||||
int SocketAdapter::SendFromConnection(ConnectionKey Key, IP_Packet* ipPkt)
|
||||
{
|
||||
BaseSession* s = nullptr;
|
||||
connections.TryGetValue(Key, &s);
|
||||
if (s != nullptr)
|
||||
return s->Send(ipPkt->GetPayload()) ? 1 : 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SocketAdapter::HandleConnectionClosed(BaseSession* sender)
|
||||
{
|
||||
ConnectionKey key = sender->key;
|
||||
connections.Remove(key);
|
||||
//Note, we delete something that is calling us
|
||||
//this is probably going to cause issues
|
||||
delete sender;
|
||||
|
||||
switch (key.protocol)
|
||||
{
|
||||
case (int)IP_Type::UDP:
|
||||
Console.WriteLn("DEV9: Socket: Closed Dead UDP Connection to %d", key.srvPort);
|
||||
break;
|
||||
case (int)IP_Type::TCP:
|
||||
Console.WriteLn("DEV9: Socket: Closed Dead TCP Connection to %d", key.srvPort);
|
||||
break;
|
||||
case (int)IP_Type::ICMP:
|
||||
Console.WriteLn("DEV9: Socket: Closed Dead ICMP Connection");
|
||||
break;
|
||||
case (int)IP_Type::IGMP:
|
||||
Console.WriteLn("DEV9: Socket: Closed Dead ICMP Connection");
|
||||
break;
|
||||
default:
|
||||
Console.WriteLn("DEV9: Socket: Closed Dead Unk Connection");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SocketAdapter::HandleFixedPortClosed(BaseSession* sender)
|
||||
{
|
||||
ConnectionKey key = sender->key;
|
||||
connections.Remove(key);
|
||||
fixedUDPPorts.Remove(key.ps2Port);
|
||||
//Note, we delete something that is calling us
|
||||
//this is probably going to cause issues
|
||||
delete sender;
|
||||
|
||||
Console.WriteLn("DEV9: Socket: Closed Dead UDP Fixed Port to %d", key.ps2Port);
|
||||
}
|
||||
|
||||
void SocketAdapter::close()
|
||||
{
|
||||
}
|
||||
|
||||
SocketAdapter::~SocketAdapter()
|
||||
{
|
||||
//Force close all sessions
|
||||
std::vector<ConnectionKey> keys = connections.GetKeys();
|
||||
DevCon.WriteLn("DEV9: Socket: Closing %d Connections", keys.size());
|
||||
for (size_t i = 0; i < keys.size(); i++)
|
||||
{
|
||||
const ConnectionKey key = keys[i];
|
||||
BaseSession* session;
|
||||
if (!connections.TryGetValue(key, &session))
|
||||
continue;
|
||||
delete session;
|
||||
}
|
||||
connections.Clear();
|
||||
fixedUDPPorts.Clear(); //fixedUDP sessions already deleted via connections
|
||||
|
||||
//Clear out vRecBuffer
|
||||
while (!vRecBuffer.IsQueueEmpty())
|
||||
{
|
||||
EthernetFrame* retPay;
|
||||
if (!vRecBuffer.Dequeue(&retPay))
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
delete retPay;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 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 PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <iphlpapi.h>
|
||||
#elif defined(__POSIX__)
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#include "net.h"
|
||||
|
||||
#include "PacketReader/IP/IP_Packet.h"
|
||||
#include "PacketReader/EthernetFrame.h"
|
||||
#include "Sessions/BaseSession.h"
|
||||
#include "SimpleQueue.h"
|
||||
#include "ThreadSafeMap.h"
|
||||
|
||||
class SocketAdapter : public NetAdapter
|
||||
{
|
||||
//SimpleQueue for ARP packages
|
||||
SimpleQueue<PacketReader::EthernetFrame*> vRecBuffer;
|
||||
|
||||
#ifdef _WIN32
|
||||
bool wsa_init = false;
|
||||
#endif
|
||||
bool initialized = false;
|
||||
PacketReader::IP::IP_Address adapterIP;
|
||||
|
||||
//Sentrys replaced by the requirment for each session class to have thread safe destructor
|
||||
|
||||
ThreadSafeMap<Sessions::ConnectionKey, Sessions::BaseSession*> connections;
|
||||
ThreadSafeMap<u16, Sessions::BaseSession*> fixedUDPPorts;
|
||||
|
||||
public:
|
||||
SocketAdapter();
|
||||
virtual bool blocks();
|
||||
virtual bool isInitialised();
|
||||
//gets a packet.rv :true success
|
||||
virtual bool recv(NetPacket* pkt);
|
||||
//sends the packet and deletes it when done (if successful).rv :true success
|
||||
virtual bool send(NetPacket* pkt);
|
||||
virtual void reset();
|
||||
virtual void reloadSettings();
|
||||
virtual void close();
|
||||
virtual ~SocketAdapter();
|
||||
static std::vector<AdapterEntry> GetAdapters();
|
||||
static AdapterOptions GetAdapterOptions();
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
bool GetWin32SelectedAdapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer);
|
||||
bool GetWin32AutoAdapter(PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer);
|
||||
#elif defined(__POSIX__)
|
||||
bool GetIfSelectedAdapter(const std::string& name, ifaddrs* adapter, ifaddrs** buffer);
|
||||
bool GetIfAutoAdapter(ifaddrs* adapter, ifaddrs** buffer);
|
||||
#endif // _WIN32
|
||||
|
||||
bool SendIP(PacketReader::IP::IP_Packet* ipPkt);
|
||||
bool SendICMP(Sessions::ConnectionKey Key, PacketReader::IP::IP_Packet* ipPkt);
|
||||
bool SendIGMP(Sessions::ConnectionKey Key, PacketReader::IP::IP_Packet* ipPkt);
|
||||
bool SendTCP(Sessions::ConnectionKey Key, PacketReader::IP::IP_Packet* ipPkt);
|
||||
bool SendUDP(Sessions::ConnectionKey Key, PacketReader::IP::IP_Packet* ipPkt);
|
||||
|
||||
int SendFromConnection(Sessions::ConnectionKey Key, PacketReader::IP::IP_Packet* ipPkt);
|
||||
|
||||
//Event must only be raised once per connection
|
||||
void HandleConnectionClosed(Sessions::BaseSession* sender);
|
||||
void HandleFixedPortClosed(Sessions::BaseSession* sender);
|
||||
};
|
|
@ -697,6 +697,7 @@ const char* Pcsx2Config::DEV9Options::NetApiNames[] = {
|
|||
"PCAP Bridged",
|
||||
"PCAP Switched",
|
||||
"TAP",
|
||||
"Sockets",
|
||||
nullptr};
|
||||
|
||||
const char* Pcsx2Config::DEV9Options::DnsModeNames[] = {
|
||||
|
|
|
@ -307,8 +307,16 @@
|
|||
<ClCompile Include="DEV9\PacketReader\IP\IP_Packet.cpp" />
|
||||
<ClCompile Include="DEV9\PacketReader\NetLib.cpp" />
|
||||
<ClCompile Include="DEV9\pcap_io.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\ICMP_Session\ICMP_Session.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_In.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_Out.cpp" />
|
||||
<ClCompile Include="DEV9\Win32\pcap_io_win32.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\BaseSession.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_Session.cpp" />
|
||||
<ClCompile Include="DEV9\smap.cpp" />
|
||||
<ClCompile Include="DEV9\sockets.cpp" />
|
||||
<ClCompile Include="DEV9\net.cpp" />
|
||||
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
||||
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
||||
|
@ -761,8 +769,16 @@
|
|||
<ClInclude Include="DEV9\PacketReader\NetLib.h" />
|
||||
<ClInclude Include="DEV9\PacketReader\Payload.h" />
|
||||
<ClInclude Include="DEV9\pcap_io.h" />
|
||||
<ClInclude Include="DEV9\Sessions\BaseSession.h" />
|
||||
<ClInclude Include="DEV9\Sessions\ICMP_Session\ICMP_Session.h" />
|
||||
<ClInclude Include="DEV9\Sessions\TCP_Session\TCP_Session.h" />
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.h" />
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_BaseSession.h" />
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_Session.h" />
|
||||
<ClInclude Include="DEV9\SimpleQueue.h" />
|
||||
<ClInclude Include="DEV9\smap.h" />
|
||||
<ClInclude Include="DEV9\sockets.h" />
|
||||
<ClInclude Include="DEV9\ThreadSafeMap.h" />
|
||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
||||
<ClInclude Include="DEV9\Win32\tap.h" />
|
||||
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
||||
|
|
|
@ -208,6 +208,15 @@
|
|||
<Filter Include="System\Ps2\DEV9\Sessions">
|
||||
<UniqueIdentifier>{64b05697-98cd-48ed-b5a5-cb0f6044ca15}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\Sessions\ICMP_Session">
|
||||
<UniqueIdentifier>{18d51d37-34bc-489c-b84f-31362bbb9243}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\Sessions\TCP_Session">
|
||||
<UniqueIdentifier>{7e1e6046-08a0-456e-8e03-47bb3813d9b0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\Sessions\UDP_Session">
|
||||
<UniqueIdentifier>{7b86e42b-816a-4d08-9de8-fa308d2b8d88}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\USB">
|
||||
<UniqueIdentifier>{df9de75c-2272-4f73-b2a0-4f9f492ba1e9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -1274,12 +1283,36 @@
|
|||
<ClCompile Include="DEV9\pcap_io.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\BaseSession.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\ICMP_Session\ICMP_Session.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\ICMP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_In.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_Out.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_Session.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Win32\pcap_io_win32.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\smap.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\sockets.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\net.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2365,12 +2398,36 @@
|
|||
<ClInclude Include="DEV9\pcap_io.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\BaseSession.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\ICMP_Session\ICMP_Session.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\ICMP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\TCP_Session\TCP_Session.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_BaseSession.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_Session.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\SimpleQueue.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\smap.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\sockets.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\ThreadSafeMap.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -170,8 +170,16 @@
|
|||
<ClCompile Include="DEV9\PacketReader\IP\IP_Packet.cpp" />
|
||||
<ClCompile Include="DEV9\PacketReader\NetLib.cpp" />
|
||||
<ClCompile Include="DEV9\pcap_io.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\ICMP_Session\ICMP_Session.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_In.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_Out.cpp" />
|
||||
<ClCompile Include="DEV9\Win32\pcap_io_win32.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\BaseSession.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.cpp" />
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_Session.cpp" />
|
||||
<ClCompile Include="DEV9\smap.cpp" />
|
||||
<ClCompile Include="DEV9\sockets.cpp" />
|
||||
<ClCompile Include="DEV9\net.cpp" />
|
||||
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
|
||||
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
|
||||
|
@ -478,8 +486,16 @@
|
|||
<ClInclude Include="DEV9\PacketReader\NetLib.h" />
|
||||
<ClInclude Include="DEV9\PacketReader\Payload.h" />
|
||||
<ClInclude Include="DEV9\pcap_io.h" />
|
||||
<ClInclude Include="DEV9\Sessions\BaseSession.h" />
|
||||
<ClInclude Include="DEV9\Sessions\ICMP_Session\ICMP_Session.h" />
|
||||
<ClInclude Include="DEV9\Sessions\TCP_Session\TCP_Session.h" />
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.h" />
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_BaseSession.h" />
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_Session.h" />
|
||||
<ClInclude Include="DEV9\SimpleQueue.h" />
|
||||
<ClInclude Include="DEV9\smap.h" />
|
||||
<ClInclude Include="DEV9\sockets.h" />
|
||||
<ClInclude Include="DEV9\ThreadSafeMap.h" />
|
||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h" />
|
||||
<ClInclude Include="DEV9\Win32\tap.h" />
|
||||
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
|
||||
|
|
|
@ -166,6 +166,15 @@
|
|||
<Filter Include="System\Ps2\DEV9\Sessions">
|
||||
<UniqueIdentifier>{64b05697-98cd-48ed-b5a5-cb0f6044ca15}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\Sessions\ICMP_Session">
|
||||
<UniqueIdentifier>{18d51d37-34bc-489c-b84f-31362bbb9243}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\Sessions\TCP_Session">
|
||||
<UniqueIdentifier>{7e1e6046-08a0-456e-8e03-47bb3813d9b0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\Sessions\UDP_Session">
|
||||
<UniqueIdentifier>{7b86e42b-816a-4d08-9de8-fa308d2b8d88}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\USB">
|
||||
<UniqueIdentifier>{df9de75c-2272-4f73-b2a0-4f9f492ba1e9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -929,12 +938,36 @@
|
|||
<ClCompile Include="DEV9\pcap_io.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\BaseSession.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\ICMP_Session\ICMP_Session.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\ICMP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_In.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_Out.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_Session.cpp">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\Win32\pcap_io_win32.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\smap.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\sockets.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DEV9\net.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1694,12 +1727,36 @@
|
|||
<ClInclude Include="DEV9\pcap_io.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\BaseSession.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\ICMP_Session\ICMP_Session.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\ICMP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\TCP_Session\TCP_Session.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_BaseSession.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_Session.h">
|
||||
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\SimpleQueue.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\smap.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\sockets.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\ThreadSafeMap.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DEV9\Win32\pcap_io_win32_funcs.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClInclude>
|
||||
|
|
Loading…
Reference in New Issue