DEV9: Add Socket api mode

This commit is contained in:
TheLastRar 2021-02-27 19:37:48 +00:00 committed by refractionpcsx2
parent 140d44d826
commit 352faeddb2
28 changed files with 4503 additions and 0 deletions

View File

@ -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;

View File

@ -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)

View File

@ -651,6 +651,7 @@ struct Pcsx2Config
PCAP_Bridged = 1,
PCAP_Switched = 2,
TAP = 3,
Sockets = 4,
};
static const char* NetApiNames[];

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

112
pcsx2/DEV9/ThreadSafeMap.h Normal file
View File

@ -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;
}
};

View File

@ -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;
}

932
pcsx2/DEV9/sockets.cpp Normal file
View File

@ -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;
}
}

86
pcsx2/DEV9/sockets.h Normal file
View File

@ -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);
};

View File

@ -697,6 +697,7 @@ const char* Pcsx2Config::DEV9Options::NetApiNames[] = {
"PCAP Bridged",
"PCAP Switched",
"TAP",
"Sockets",
nullptr};
const char* Pcsx2Config::DEV9Options::DnsModeNames[] = {

View File

@ -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" />

View File

@ -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>

View File

@ -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" />

View File

@ -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>