mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
375 lines
9.6 KiB
C++
375 lines
9.6 KiB
C++
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#include <algorithm>
|
|
#ifdef __POSIX__
|
|
#include <string>
|
|
#include <vector>
|
|
#include <fstream>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#if defined(__FreeBSD__) || (__APPLE__)
|
|
#include <sys/param.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "DHCP_Server.h"
|
|
#include "DEV9/PacketReader/IP/UDP/UDP_Packet.h"
|
|
#include "DEV9/PacketReader/IP/UDP/DHCP/DHCP_Packet.h"
|
|
|
|
#include "DEV9/DEV9.h"
|
|
#include "DEV9/AdapterUtils.h"
|
|
|
|
using namespace PacketReader;
|
|
using namespace PacketReader::IP;
|
|
using namespace PacketReader::IP::UDP;
|
|
using namespace PacketReader::IP::UDP::DHCP;
|
|
|
|
namespace InternalServers
|
|
{
|
|
DHCP_Server::DHCP_Server(std::function<void()> receivedcallback)
|
|
: callback{receivedcallback}
|
|
{
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
void DHCP_Server::Init(PIP_ADAPTER_ADDRESSES adapter, IP_Address ipOverride, IP_Address subnetOverride, IP_Address gatewayOverride)
|
|
#elif defined(__POSIX__)
|
|
void DHCP_Server::Init(ifaddrs* adapter, IP_Address ipOverride, IP_Address subnetOverride, IP_Address gatewayOverride)
|
|
#endif
|
|
{
|
|
netmask = {};
|
|
gateway = {};
|
|
dns1 = {};
|
|
dns2 = {};
|
|
broadcastIP = {};
|
|
|
|
if (ipOverride.integer != 0)
|
|
ps2IP = ipOverride;
|
|
else
|
|
ps2IP = *(IP_Address*)&EmuConfig.DEV9.PS2IP;
|
|
|
|
if (subnetOverride.integer != 0)
|
|
netmask = subnetOverride;
|
|
else if (EmuConfig.DEV9.AutoMask)
|
|
AutoNetmask(adapter);
|
|
else
|
|
netmask = *(IP_Address*)EmuConfig.DEV9.Mask;
|
|
|
|
if (gatewayOverride.integer != 0)
|
|
gateway = gatewayOverride;
|
|
else if (EmuConfig.DEV9.AutoGateway)
|
|
AutoGateway(adapter);
|
|
else
|
|
gateway = *(IP_Address*)EmuConfig.DEV9.Gateway;
|
|
|
|
switch (EmuConfig.DEV9.ModeDNS1)
|
|
{
|
|
case Pcsx2Config::DEV9Options::DnsMode::Manual:
|
|
dns1 = *(IP_Address*)EmuConfig.DEV9.DNS1;
|
|
break;
|
|
case Pcsx2Config::DEV9Options::DnsMode::Internal:
|
|
dns1 = {{{192, 0, 2, 1}}};
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (EmuConfig.DEV9.ModeDNS2)
|
|
{
|
|
case Pcsx2Config::DEV9Options::DnsMode::Manual:
|
|
dns2 = *(IP_Address*)EmuConfig.DEV9.DNS2;
|
|
break;
|
|
case Pcsx2Config::DEV9Options::DnsMode::Internal:
|
|
dns2 = {{{192, 0, 2, 1}}};
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
AutoDNS(adapter, EmuConfig.DEV9.ModeDNS1 == Pcsx2Config::DEV9Options::DnsMode::Auto, EmuConfig.DEV9.ModeDNS2 == Pcsx2Config::DEV9Options::DnsMode::Auto);
|
|
AutoBroadcast(ps2IP, netmask);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
void DHCP_Server::AutoNetmask(PIP_ADAPTER_ADDRESSES adapter)
|
|
{
|
|
if (adapter != nullptr)
|
|
{
|
|
PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress;
|
|
while (address != nullptr && address->Address.lpSockaddr->sa_family != AF_INET)
|
|
address = address->Next;
|
|
|
|
if (address != nullptr)
|
|
{
|
|
ULONG mask;
|
|
if (ConvertLengthToIpv4Mask(address->OnLinkPrefixLength, &mask) == NO_ERROR)
|
|
netmask.integer = mask;
|
|
}
|
|
}
|
|
}
|
|
#elif defined(__POSIX__)
|
|
void DHCP_Server::AutoNetmask(ifaddrs* adapter)
|
|
{
|
|
if (adapter != nullptr)
|
|
{
|
|
if (adapter->ifa_netmask != nullptr && adapter->ifa_netmask->sa_family == AF_INET)
|
|
{
|
|
sockaddr_in* sockaddr = (sockaddr_in*)adapter->ifa_netmask;
|
|
netmask = *(IP_Address*)&sockaddr->sin_addr;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
void DHCP_Server::AutoGateway(PIP_ADAPTER_ADDRESSES adapter)
|
|
#elif defined(__POSIX__)
|
|
void DHCP_Server::AutoGateway(ifaddrs* adapter)
|
|
#endif
|
|
{
|
|
std::vector<IP_Address> gateways = AdapterUtils::GetGateways(adapter);
|
|
|
|
if (gateways.size() > 0)
|
|
gateway = gateways[0];
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
void DHCP_Server::AutoDNS(PIP_ADAPTER_ADDRESSES adapter, bool autoDNS1, bool autoDNS2)
|
|
#elif defined(__POSIX__)
|
|
void DHCP_Server::AutoDNS(ifaddrs* adapter, bool autoDNS1, bool autoDNS2)
|
|
#endif
|
|
{
|
|
std::vector<IP_Address> dnsIPs = AdapterUtils::GetDNS(adapter);
|
|
|
|
if (autoDNS1)
|
|
{
|
|
//Auto DNS1
|
|
//If Adapter has DNS, add 1st entry
|
|
if (dnsIPs.size() >= 1)
|
|
dns1 = dnsIPs[0];
|
|
|
|
//Auto DNS1 & AutoDNS2
|
|
if (autoDNS2 && dnsIPs.size() >= 2)
|
|
dns2 = dnsIPs[1];
|
|
}
|
|
else if (autoDNS2)
|
|
{
|
|
//Manual DNS1 & Auto DNS2
|
|
|
|
//Use adapter's DNS2 if it has one
|
|
//otherwise use adapter's DNS1
|
|
|
|
if (!dnsIPs.empty())
|
|
dns2 = dnsIPs[std::min<size_t>(1, dnsIPs.size() - 1)];
|
|
}
|
|
if (dns1.integer == 0 && dns2.integer != 0)
|
|
{
|
|
Console.Error("DHCP: DNS1 is zero, but DNS2 is valid, using DNS2 as DNS1");
|
|
//no value for DNS1, but we have a value for DNS2
|
|
//set DNS1 to DNS2 and zero DNS2
|
|
dns1 = dns2;
|
|
dns2 = {};
|
|
}
|
|
}
|
|
|
|
void DHCP_Server::AutoBroadcast(IP_Address parPS2IP, IP_Address parNetmask)
|
|
{
|
|
if (parNetmask.integer != 0)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
broadcastIP.bytes[i] = ((parPS2IP.bytes[i]) | (~parNetmask.bytes[i]));
|
|
}
|
|
}
|
|
|
|
UDP_Packet* DHCP_Server::Recv()
|
|
{
|
|
UDP_Packet* payload;
|
|
if (recvBuff.Dequeue(&payload))
|
|
return payload;
|
|
return nullptr;
|
|
}
|
|
|
|
bool DHCP_Server::Send(UDP_Packet* payload)
|
|
{
|
|
UDP_Packet* udpPacket = static_cast<UDP_Packet*>(payload);
|
|
PayloadPtr* udpPayload = static_cast<PayloadPtr*>(udpPacket->GetPayload());
|
|
DHCP_Packet dhcp = DHCP_Packet(udpPayload->data, udpPayload->GetLength());
|
|
|
|
//State
|
|
u8 hType = dhcp.hardwareType;
|
|
u8 hLen = dhcp.hardwareAddressLength;
|
|
u32 xID = dhcp.transactionID;
|
|
u8* cMac = dhcp.clientHardwareAddress;
|
|
u32 cookie = dhcp.magicCookie;
|
|
|
|
u8 msg = 0;
|
|
std::vector<u8> reqList;
|
|
|
|
uint leaseTime = 86400;
|
|
|
|
for (size_t i = 0; i < dhcp.options.size(); i++)
|
|
{
|
|
switch (dhcp.options[i]->GetCode())
|
|
{
|
|
case 0:
|
|
continue;
|
|
case 1:
|
|
if (netmask != ((DHCPopSubnet*)dhcp.options[i])->subnetMask)
|
|
Console.Error("DHCP: SubnetMask missmatch");
|
|
break;
|
|
case 3:
|
|
if (((DHCPopRouter*)dhcp.options[i])->routers.size() != 1)
|
|
Console.Error("DHCP: Routers count missmatch");
|
|
|
|
if (gateway != ((DHCPopRouter*)dhcp.options[i])->routers[0])
|
|
Console.Error("DHCP: RouterIP missmatch");
|
|
break;
|
|
case 6:
|
|
// clang-format off
|
|
if (((((DHCPopDNS*)dhcp.options[i])->dnsServers.size() == 0 && dns1.integer == 0) ||
|
|
(((DHCPopDNS*)dhcp.options[i])->dnsServers.size() == 1 && dns1.integer != 0 && dns2.integer == 0) ||
|
|
(((DHCPopDNS*)dhcp.options[i])->dnsServers.size() == 2 && dns2.integer != 0)) == false)
|
|
Console.Error("DHCP: DNS count missmatch");
|
|
// clang-format on
|
|
|
|
if ((((DHCPopDNS*)dhcp.options[i])->dnsServers.size() > 0 && dns1 != ((DHCPopDNS*)dhcp.options[i])->dnsServers[0]) ||
|
|
(((DHCPopDNS*)dhcp.options[i])->dnsServers.size() > 1 && dns2 != ((DHCPopDNS*)dhcp.options[i])->dnsServers[1]))
|
|
Console.Error("DHCP: DNS missmatch");
|
|
break;
|
|
case 12:
|
|
//TODO use name?
|
|
break;
|
|
case 50:
|
|
if (ps2IP != ((DHCPopREQIP*)dhcp.options[i])->requestedIP)
|
|
Console.Error("DHCP: ReqIP missmatch");
|
|
break;
|
|
case 51:
|
|
leaseTime = ((DHCPopIPLT*)(dhcp.options[i]))->ipLeaseTime;
|
|
break;
|
|
case 53:
|
|
msg = ((DHCPopMSG*)(dhcp.options[i]))->message;
|
|
break;
|
|
case 54:
|
|
if (NetAdapter::internalIP != ((DHCPopSERVIP*)dhcp.options[i])->serverIP)
|
|
Console.Error("DHCP: ServIP missmatch");
|
|
break;
|
|
case 55:
|
|
reqList = ((DHCPopREQLIST*)(dhcp.options[i]))->requests;
|
|
break;
|
|
case 56: //String message
|
|
break;
|
|
case 57:
|
|
maxMs = ((DHCPopMMSGS*)(dhcp.options[i]))->maxMessageSize;
|
|
break;
|
|
case 60: //ClassID
|
|
case 61: //ClientID
|
|
case 255: //End
|
|
break;
|
|
default:
|
|
Console.Error("DHCP: Got Unhandled Option %d", dhcp.options[i]->GetCode());
|
|
break;
|
|
}
|
|
}
|
|
|
|
DHCP_Packet* retPay = new DHCP_Packet();
|
|
retPay->op = 2,
|
|
retPay->hardwareType = hType,
|
|
retPay->hardwareAddressLength = hLen,
|
|
retPay->transactionID = xID,
|
|
|
|
retPay->yourIP = ps2IP;
|
|
retPay->serverIP = NetAdapter::internalIP;
|
|
|
|
memcpy(retPay->clientHardwareAddress, cMac, 6);
|
|
retPay->magicCookie = cookie;
|
|
|
|
if (msg == 1 || msg == 3) //Fill out Requests
|
|
{
|
|
if (msg == 1)
|
|
retPay->options.push_back(new DHCPopMSG(2));
|
|
if (msg == 3)
|
|
retPay->options.push_back(new DHCPopMSG(5));
|
|
|
|
for (size_t i = 0; i < reqList.size(); i++)
|
|
{
|
|
switch (reqList[i])
|
|
{
|
|
case 1:
|
|
retPay->options.push_back(new DHCPopSubnet(netmask));
|
|
break;
|
|
case 3:
|
|
if (gateway.integer != 0)
|
|
{
|
|
std::vector<IP_Address> routers;
|
|
routers.push_back(gateway);
|
|
retPay->options.push_back(new DHCPopRouter(routers));
|
|
}
|
|
break;
|
|
case 6:
|
|
if (dns1.integer != 0)
|
|
{
|
|
std::vector<IP_Address> dns;
|
|
dns.push_back(dns1);
|
|
if (dns2.integer != 0)
|
|
dns.push_back(dns2);
|
|
retPay->options.push_back(new DHCPopDNS(dns));
|
|
}
|
|
break;
|
|
case 15:
|
|
retPay->options.push_back(new DHCPopDnsName("PCSX2"));
|
|
break;
|
|
case 28:
|
|
retPay->options.push_back(new DHCPopBCIP(broadcastIP));
|
|
break;
|
|
case 50:
|
|
retPay->options.push_back(new DHCPopREQIP(ps2IP));
|
|
break;
|
|
case 53: //Msg (Already added)
|
|
case 54: //Server Identifier (Already Added)
|
|
break;
|
|
default:
|
|
Console.Error("DHCP: Got Unhandled Request %d", reqList[i]);
|
|
break;
|
|
}
|
|
}
|
|
retPay->options.push_back(new DHCPopIPLT(leaseTime));
|
|
}
|
|
else if (msg == 7)
|
|
return true;
|
|
|
|
retPay->options.push_back(new DHCPopSERVIP(NetAdapter::internalIP));
|
|
retPay->options.push_back(new DHCPopEND());
|
|
|
|
retPay->maxLength = maxMs;
|
|
UDP_Packet* retUdp = new UDP_Packet(retPay);
|
|
retUdp->sourcePort = 67;
|
|
retUdp->destinationPort = 68;
|
|
|
|
recvBuff.Enqueue(retUdp);
|
|
callback();
|
|
return true;
|
|
}
|
|
|
|
DHCP_Server::~DHCP_Server()
|
|
{
|
|
//Delete entries in queue
|
|
while (!recvBuff.IsQueueEmpty())
|
|
{
|
|
UDP_Packet* retPay = nullptr;
|
|
if (!recvBuff.Dequeue(&retPay))
|
|
{
|
|
std::this_thread::yield();
|
|
continue;
|
|
}
|
|
|
|
delete retPay;
|
|
}
|
|
}
|
|
} // namespace InternalServers
|