Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infrastructure Auto DNS: Preconfigured per-game infrastructure DNS through JSON #19865

Merged
merged 7 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2743,6 +2743,7 @@ set(NativeAssets
assets/Roboto-Condensed.ttf
assets/7z.png
assets/compat.ini
assets/infra-dns.json
assets/gamecontrollerdb.txt
assets/langregion.ini
assets/ppge_atlas.zim
Expand Down
21 changes: 13 additions & 8 deletions Common/Net/Resolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,19 +322,24 @@ struct DNSHeader {
};

// Function to convert a domain name to DNS query format
// http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm
static void encode_domain_name(const char *domain, unsigned char *encoded) {
const char *pos = domain;
unsigned char *ptr = encoded;

while (*pos) {
const char *start = pos;
while (*pos && *pos != '.') pos++;
while (*pos && *pos != '.') {
pos++;
}

*ptr++ = (unsigned char)(pos - start);
*ptr++ = (unsigned char)(pos - start); // length field
memcpy(ptr, start, pos - start);
ptr += pos - start;

if (*pos == '.') pos++;
if (*pos == '.') {
pos++;
}
}
*ptr = 0; // End of domain name
}
Expand Down Expand Up @@ -418,20 +423,20 @@ bool DirectDNSLookupIPV4(const char *dns_server_ip, const char *domain, uint32_t
return false;
}

int sockfd;
SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// Create UDP socket
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
if (sockfd < 0) {
ERROR_LOG(Log::sceNet, "Socket creation for direct DNS failed");
return 1;
}

struct sockaddr_in server_addr {};
struct sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(DNS_PORT);

if (net::inet_pton(AF_INET, dns_server_ip, &server_addr.sin_addr) <= 0) {
ERROR_LOG(Log::sceNet,"Invalid DNS server IP address %s", dns_server_ip);
close(sockfd);
closesocket(sockfd);
return 1;
}

Expand All @@ -451,7 +456,7 @@ bool DirectDNSLookupIPV4(const char *dns_server_ip, const char *domain, uint32_t

// Send DNS query
size_t query_len = sizeof(DNSHeader) + (qinfo - buffer) + 4;
if (sendto(sockfd, (const char *)buffer, query_len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
if (sendto(sockfd, (const char *)buffer, (int)query_len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
ERROR_LOG(Log::sceNet, "Failed to send DNS query");
closesocket(sockfd);
return 1;
Expand Down
4 changes: 2 additions & 2 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,13 +901,13 @@ static const ConfigSetting networkSettings[] = {
ConfigSetting("EnableAdhocServer", &g_Config.bEnableAdhocServer, false, CfgFlag::PER_GAME),
ConfigSetting("proAdhocServer", &g_Config.proAdhocServer, "socom.cc", CfgFlag::PER_GAME),
ConfigSetting("PortOffset", &g_Config.iPortOffset, 10000, CfgFlag::PER_GAME),
ConfigSetting("PrimaryDNSServer", &g_Config.primaryDNSServer, "67.222.156.250", CfgFlag::PER_GAME),
ConfigSetting("SecondaryDNSServer", &g_Config.secondaryDNSServer, "0.0.0.0", CfgFlag::PER_GAME),
ConfigSetting("PrimaryDNSServer", &g_Config.sInfrastructureDNSServer, "67.222.156.250", CfgFlag::PER_GAME),
ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 0, CfgFlag::PER_GAME),
ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, CfgFlag::PER_GAME),
ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, CfgFlag::PER_GAME),
ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, false, CfgFlag::PER_GAME),
ConfigSetting("InfrastructureUsername", &g_Config.sInfrastructureUsername, &DefaultInfrastructureUsername, CfgFlag::PER_GAME),
ConfigSetting("InfrastructureAutoDNS", &g_Config.bInfrastructureAutoDNS, true, CfgFlag::PER_GAME),

ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, CfgFlag::PER_GAME),
ConfigSetting("ChatButtonPosition", &g_Config.iChatButtonPosition, (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
Expand Down
11 changes: 6 additions & 5 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -457,13 +457,14 @@ struct Config {
bool bSavedataUpgrade;

// Networking
bool bEnableAdhocServer;
std::string proAdhocServer;
std::string primaryDNSServer;
std::string secondaryDNSServer;
std::string sInfrastructureDNSServer;
std::string sInfrastructureUsername; // Username used for Infrastructure play. Different restrictions.
bool bInfrastructureAutoDNS;

bool bEnableWlan;
std::map<std::string, std::string> mHostToAlias; // TODO: mPostShaderSetting
bool bEnableAdhocServer;
std::map<std::string, std::string> mHostToAlias; // Local DNS database stored in ini file
bool bEnableUPnP;
bool bUPnPUseOriginalPort;
bool bForcedFirstConnect;
Expand All @@ -472,7 +473,7 @@ struct Config {
int iWlanAdhocChannel;
bool bWlanPowerSave;
bool bEnableNetworkChat;
//for chat position , moveable buttons is better than this

int iChatButtonPosition;
int iChatScreenPosition;

Expand Down
1 change: 1 addition & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,7 @@
<ClInclude Include="MIPS\x86\X64IRRegCache.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\assets\infra-dns.json" />
<None Include="..\ext\cityhash\COPYING" />
<None Include="..\ext\cityhash\README" />
<None Include="..\ext\gason\LICENSE" />
Expand Down
1 change: 1 addition & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,7 @@
<None Include="..\ext\libzip\Android.mk">
<Filter>Ext\libzip</Filter>
</None>
<None Include="..\assets\infra-dns.json" />
</ItemGroup>
<ItemGroup>
<Text Include="..\ext\sha1\CMakeLists.txt">
Expand Down
146 changes: 144 additions & 2 deletions Core/HLE/sceNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
#include "Common/Net/Resolve.h"
#include "Common/Net/SocketCompat.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/File/VFS/VFS.h"

#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/Serialize/SerializeMap.h"
#include "Common/System/OSD.h"
#include "Common/Data/Format/JSONReader.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceKernelMemory.h"
Expand Down Expand Up @@ -87,6 +90,9 @@ int actionAfterApctlMipsCall;
std::recursive_mutex apctlEvtMtx;
std::deque<ApctlArgs> apctlEvents;

// Loaded auto-config
InfraDNSConfig g_infraDNSConfig;

u32 Net_Term();
int NetApctl_Term();
void NetApctl_InitDefaultInfo();
Expand Down Expand Up @@ -129,6 +135,99 @@ void AfterApctlMipsCall::SetData(int HandlerID, int OldState, int NewState, int
argsAddr = ArgsAddr;
}

bool LoadDNSForGameID(std::string_view gameID, InfraDNSConfig *dns) {
using namespace json;

// TODO: Load from cache instead of zip (if possible), and sometimes update it.
size_t jsonSize;
std::unique_ptr<uint8_t[]> data(g_VFS.ReadFile("infra-dns.json", &jsonSize));
if (!data) {
return false;
}

std::string_view jsonStr = std::string_view((const char *)data.get(), jsonSize);
json::JsonReader reader(jsonStr.data(), jsonStr.length());
if (!reader.ok() || !reader.root()) {
ERROR_LOG(Log::IO, "Error parsing DNS JSON");
return false;
}

const JsonGet root = reader.root();
const JsonGet def = root.getDict("default");

// Load the default DNS.
dns->dns = def.getStringOr("dns", "");;

const JsonNode *games = root.getArray("games");
for (const JsonNode *iter : games->value) {
JsonGet game = iter->value;
// Goddamn I have to change the json reader we're using. So ugly.
const JsonNode *workingIdsNode = game.getArray("known_working_ids");
const JsonNode *otherIdsNode = game.getArray("other_ids");
const JsonNode *notWorkingIdsNode = game.getArray("not_working_ids");
if (!workingIdsNode) {
// We ignore this game.
continue;
}

bool found = false;

std::vector<std::string> ids;
JsonGet(workingIdsNode->value).getStringVector(&ids);
for (auto &id : ids) {
if (id == gameID) {
found = true;
dns->state = InfraGameState::Working;
break;
}
}
if (!found && notWorkingIdsNode) {
// Check the non working array
JsonGet(notWorkingIdsNode->value).getStringVector(&ids);
for (auto &id : ids) {
if (id == gameID) {
found = true;
dns->state = InfraGameState::NotWorking;
break;
}
}
}
if (!found && otherIdsNode) {
// Check the "other" array, not sure what we do with these.
JsonGet(otherIdsNode->value).getStringVector(&ids);
for (auto &id : ids) {
if (id == gameID) {
found = true;
dns->state = InfraGameState::Unknown;
break;
}
}
}

if (!found) {
continue;
}

std::string name = game.getStringOr("name", "");
std::string dyn_dns = game.getStringOr("dyn_dns", dns->dns.c_str());
dns->dns = game.getStringOr("dns", dns->dns.c_str());
dns->dyn_dns = game.getStringOr("dyn_dns", "");
if (game.hasChild("domains", JSON_OBJECT)) {
const JsonGet domains = game.getDict("domains");
for (auto iter : domains.value_) {
std::string domain = std::string(iter->key);
std::string ipAddr = std::string(iter->value.toString());
dns->fixedDNS[domain] = ipAddr;
}
}

// TODO: Check for not working platforms
break;
}

return true;
}

void InitLocalhostIP() {
// The entire 127.*.*.* is reserved for loopback.
uint32_t localIP = 0x7F000001 + PPSSPP_ID - 1;
Expand Down Expand Up @@ -549,6 +648,7 @@ u32 Net_Term() {
FreeUser(netThread2Addr);
netInited = false;

g_infraDNSConfig = {};
return 0;
}

Expand Down Expand Up @@ -613,6 +713,48 @@ static int sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netini
// Clear Socket Translator Memory
memset(&adhocSockets, 0, sizeof(adhocSockets));

if (g_Config.bInfrastructureAutoDNS) {
// Load the automatic DNS config for this game - or the defaults.
std::string discID = g_paramSFO.GetDiscID();
LoadDNSForGameID(discID, &g_infraDNSConfig);

// If dyn_dns is non-empty, try to use it to replace the specified DNS.
// If fails, we just use the dns. TODO: Do this in the background somehow...
const auto &dns = g_infraDNSConfig.dns;
const auto &dyn_dns = g_infraDNSConfig.dyn_dns;
if (!dyn_dns.empty()) {
// Try to look it up in system DNS
INFO_LOG(Log::sceNet, "DynDNS requested, trying to resolve '%s'...", dyn_dns.c_str());
addrinfo *resolved = nullptr;
std::string err;
if (!net::DNSResolve(dyn_dns, "", &resolved, err)) {
ERROR_LOG(Log::sceNet, "Error resolving, falling back to '%s'", dns.c_str());
} else if (resolved) {
bool found = false;
for (auto ptr = resolved; ptr && !found; ptr = ptr->ai_next) {
switch (ptr->ai_family) {
case AF_INET:
{
char ipstr[256];
if (inet_ntop(ptr->ai_family, &(((struct sockaddr_in*)ptr->ai_addr)->sin_addr), ipstr, sizeof(ipstr)) != 0) {
INFO_LOG(Log::sceNet, "Successfully resolved '%s' to '%s', overriding DNS.", dyn_dns.c_str(), ipstr);
if (g_infraDNSConfig.dns != ipstr) {
WARN_LOG(Log::sceNet, "Replacing specified DNS IP %s with dyndns %s!", g_infraDNSConfig.dns.c_str(), ipstr);
g_infraDNSConfig.dns = ipstr;
} else {
INFO_LOG(Log::sceNet, "DynDNS: %s already up to date", g_infraDNSConfig.dns.c_str());
}
found = true;
}
break;
}
}
}
net::DNSResolveFree(resolved);
}
}
}

netInited = true;
return hleLogSuccessI(Log::sceNet, 0);
}
Expand Down Expand Up @@ -785,10 +927,10 @@ void NetApctl_InitInfo(int confId) {
truncate_cpy(netApctlInfo.gateway, sizeof(netApctlInfo.gateway), ipstr);
// We should probably use public DNS Server instead of localhost IP since most people don't have DNS Server running on localhost (ie. Untold Legends The Warrior's Code is trying to lookup dns using the primary dns), but accessing public DNS Server from localhost may result to ENETUNREACH error if the gateway can't access the public server (ie. using SO_DONTROUTE)
//if (strcmp(ipstr, "127.0.0.1") == 0)
truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), g_Config.primaryDNSServer.c_str()); // Private Servers may need to use custom DNS Server
truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), g_Config.sInfrastructureDNSServer.c_str()); // Private Servers may need to use custom DNS Server
//else
// truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), ipstr);
truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), g_Config.secondaryDNSServer.c_str()); // Fireteam Bravo 2 seems to try to use secondary DNS too if it's not 0.0.0.0
truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), "0.0.0.0"); // Fireteam Bravo 2 seems to try to use secondary DNS too if it's not 0.0.0.0
truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "255.255.255.0");
}

Expand Down
21 changes: 19 additions & 2 deletions Core/HLE/sceNet.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,30 @@ void Register_sceWlanDrv();
void Register_sceNetUpnp();
void Register_sceNetIfhandle();


void __NetInit();
void __NetShutdown();
void __NetDoState(PointerWrap &p);

int NetApctl_GetState();

int sceNetApctlConnect(int connIndex);
int sceNetInetPoll(void *fds, u32 nfds, int timeout);
int sceNetApctlTerm();

enum class InfraGameState {
Unknown,
Working,
NotWorking,
};

// Loaded an interpreted for a specific game from the JSON - doesn't represent the entire JSON.
struct InfraDNSConfig {
std::string gameName;
std::string dns;
std::string dyn_dns;
InfraGameState state;
std::map<std::string, std::string> fixedDNS;
int score;
std::string comment;
};

extern InfraDNSConfig g_infraDNSConfig;
Loading
Loading