Skip to content

Commit

Permalink
Custom port support implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
XITRIX committed Oct 17, 2024
1 parent bb7584e commit df8e858
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 126 deletions.
19 changes: 6 additions & 13 deletions app/platforms/ios/apple_mdns.mm
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ - (void)netServiceDidResolveAddress:(NSNetService *)service {
}

Host host;
NSString* tempIp;
NSString* hostAddress;

// First, look for an IPv4 record for the local address
for (NSData* addrData in addresses) {
Expand All @@ -190,33 +190,26 @@ - (void)netServiceDidResolveAddress:(NSNetService *)service {
continue;
}

tempIp = [MDNSManager sockAddrToString:addrData];
hostAddress = [MDNSManager sockAddrToString:addrData];
// brls::Logger::info("Local address chosen: {} -> {}", [service hostName], host.localAddress);
break;
}

if (tempIp == nil) {
if (hostAddress == nil) {
// If we didn't find an IPv4 record, look for a local IPv6 record
for (NSData* addrData in addresses) {
if ([MDNSManager isLocalIpv6Address:addrData]) {
tempIp = [MDNSManager sockAddrToString:addrData];
hostAddress = [MDNSManager sockAddrToString:addrData];
// brls::Logger::info("Local address chosen: {} -> {}", [service hostName], host.localAddress);
break;
}
}
}

// host.ipv6Address = [MDNSManager getBestIpv6Address:addresses];
// brls::Logger::info("IPv6 address chosen: {} -> {}", [service hostName], host.ipv6Address);

// host.activeAddress = host.localAddress;
auto hostAddress = [tempIp componentsSeparatedByString:@":"];
NSString* ipAddress = hostAddress[0];
NSString* port = hostAddress[1];

if (hostAddress == nil) return;
auto hostName = [service.hostName stringByReplacingOccurrencesOfString:@".local." withString:@""];

host.address = std::string([ipAddress UTF8String]);
host.address = std::string([hostAddress UTF8String]);
host.hostname = std::string([hostName UTF8String]);
foundHosts.push_back(host);

Expand Down
4 changes: 2 additions & 2 deletions app/src/add_host_tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ AddHostTab::AddHostTab() {
this->inflateFromXMLRes("xml/tabs/add_host.xml");

hostIP->init("add_host/host_ip"_i18n, "");
hostIP->setPlaceholder("192.168.1.109");
hostIP->setHint("192.168.1.109");
hostIP->setPlaceholder("192.168.1.109:47989");
hostIP->setHint("192.168.1.109:47989");

connect->setText("add_host/connect"_i18n);
connect->registerClickAction([this](View* view) {
Expand Down
219 changes: 134 additions & 85 deletions app/src/libgamestream/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sstream>

#define CHANNEL_COUNT_STEREO 2
#define CHANNEL_COUNT_51_SURROUND 6
Expand All @@ -37,99 +38,120 @@

static std::string unique_id = "0123456789ABCDEF";

static int load_server_status(PSERVER_DATA server, bool skip_https) {
int ret;
static int load_serverinfo(PSERVER_DATA server, bool https) {
int ret = GS_INVALID;
char url[4096];
int i = skip_https ? 1 : 0;
std::string pairedText;
std::string currentGameText;
std::string stateText;
std::string httpsPortText;

do {
std::string pairedText;
std::string currentGameText;
std::string stateText;
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS
// if the client is not already paired. Since we can't pair without
// knowing the server version, we make another request over HTTP if the
// HTTPS request fails. We can't just use HTTP for everything because it
// doesn't accurately tell us if we're paired.

ret = GS_INVALID;
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s",
https ? "https" : "http", server->serverInfo.address,
https ? server->httpsPort : server->httpPort, unique_id.c_str());

// Modern GFE versions don't allow serverinfo to be fetched over HTTPS
// if the client is not already paired. Since we can't pair without
// knowing the server version, we make another request over HTTP if the
// HTTPS request fails. We can't just use HTTP for everything because it
// doesn't accurately tell us if we're paired.
Data data;

snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s",
i == 0 ? "https" : "http", server->serverInfo.address,
i == 0 ? 47984 : 47989, unique_id.c_str());
if (http_request(url, &data, HTTPRequestTimeoutLow) != GS_OK) {
ret = GS_IO_ERROR;
goto cleanup;
}

Data data;
if (xml_status(data) == GS_ERROR) {
ret = GS_ERROR;
goto cleanup;
}

if (http_request(url, &data, HTTPRequestTimeoutLow) != GS_OK) {
ret = GS_IO_ERROR;
goto cleanup;
}
if (xml_search(data, "currentgame", &currentGameText) != GS_OK) {
goto cleanup;
}

if (xml_status(data) == GS_ERROR) {
ret = GS_ERROR;
goto cleanup;
}
if (xml_search(data, "PairStatus", &pairedText) != GS_OK)
goto cleanup;

if (xml_search(data, "currentgame", &currentGameText) != GS_OK) {
goto cleanup;
}
if (xml_search(data, "appversion", &server->serverInfoAppVersion) !=
GS_OK) {
goto cleanup;
}

if (xml_search(data, "PairStatus", &pairedText) != GS_OK)
goto cleanup;
if (xml_search(data, "state", &stateText) != GS_OK)
goto cleanup;

if (xml_search(data, "appversion", &server->serverInfoAppVersion) !=
GS_OK) {
goto cleanup;
}
if (xml_search(data, "ServerCodecModeSupport",
&server->serverInfo.serverCodecModeSupport) != GS_OK)
goto cleanup;

if (xml_search(data, "state", &stateText) != GS_OK)
goto cleanup;
if (xml_search(data, "gputype", &server->gpuType) != GS_OK)
goto cleanup;

if (xml_search(data, "ServerCodecModeSupport",
&server->serverInfo.serverCodecModeSupport) != GS_OK)
goto cleanup;
if (xml_search(data, "GsVersion", &server->gsVersion) != GS_OK)
goto cleanup;

if (xml_search(data, "gputype", &server->gpuType) != GS_OK)
goto cleanup;
if (xml_search(data, "hostname", &server->hostname) != GS_OK)
goto cleanup;

if (xml_search(data, "GsVersion", &server->gsVersion) != GS_OK)
goto cleanup;
if (xml_search(data, "GfeVersion", &server->serverInfoGfeVersion) != GS_OK)
goto cleanup;

if (xml_search(data, "hostname", &server->hostname) != GS_OK)
goto cleanup;
if (xml_search(data, "HttpsPort", &httpsPortText) != GS_OK)
goto cleanup;

if (xml_search(data, "GfeVersion", &server->serverInfoGfeVersion) !=
GS_OK)
goto cleanup;
if (xml_search(data, "mac", &server->mac) != GS_OK)
goto cleanup;

if (xml_search(data, "mac", &server->mac) != GS_OK)
goto cleanup;
// These fields are present on all version of GFE that this client
// supports
if (currentGameText.empty() || pairedText.empty() ||
server->serverInfoAppVersion.empty() || stateText.empty()) {
goto cleanup;
}

// These fields are present on all version of GFE that this client
// supports
if (currentGameText.empty() || pairedText.empty() ||
server->serverInfoAppVersion.empty() || stateText.empty()) {
goto cleanup;
}
server->paired = pairedText == "1";
server->currentGame =
currentGameText.empty() ? 0 : atoi(currentGameText.c_str());
server->supports4K = server->serverInfo.serverCodecModeSupport != 0;
server->serverMajorVersion = atoi(server->serverInfoAppVersion.c_str());
server->httpsPort = atoi(httpsPortText.c_str());
if (!server->httpsPort)
server->httpsPort = 47984;

server->paired = pairedText == "1";
server->currentGame =
currentGameText.empty() ? 0 : atoi(currentGameText.c_str());
server->supports4K = server->serverInfo.serverCodecModeSupport != 0;
server->serverMajorVersion = atoi(server->serverInfoAppVersion.c_str());

if (stateText == "_SERVER_BUSY") {
// After GFE 2.8, current game remains set even after streaming
// has ended. We emulate the old behavior by forcing it to zero
// if streaming is not active.
server->currentGame = 0;
}
ret = GS_OK;
if (stateText == "_SERVER_BUSY") {
// After GFE 2.8, current game remains set even after streaming
// has ended. We emulate the old behavior by forcing it to zero
// if streaming is not active.
server->currentGame = 0;
}
ret = GS_OK;

cleanup:
return ret;
}

static int load_server_status(PSERVER_DATA server) {
int ret = GS_INVALID;
int i;

/* Fetch the HTTPS port if we don't have one yet */
if (!server->httpsPort) {
ret = load_serverinfo(server, false);
if (ret != GS_OK)
return ret;
}

cleanup:
i++;
} while (ret != GS_OK && i < 2);
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
// is not already paired. Since we can't pair without knowing the server version, we
// make another request over HTTP if the HTTPS request fails. We can't just use HTTP
// for everything because it doesn't accurately tell us if we're paired.
ret = GS_INVALID;
for (i = 0; i < 2 && ret != GS_OK; i++) {
ret = load_serverinfo(server, i == 0);
}

if (ret == GS_OK) {
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
Expand Down Expand Up @@ -165,8 +187,10 @@ int gs_unpair(PSERVER_DATA server) {

Data data;

snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s",
server->serverInfo.address, unique_id.c_str());
snprintf(url, sizeof(url), "http://%s:%u/unpair?uniqueid=%s",
server->serverInfo.address,
server->httpPort,
unique_id.c_str());
ret = http_request(url, &data, HTTPRequestTimeoutLow);
return ret;
}
Expand Down Expand Up @@ -223,10 +247,12 @@ int gs_pair(PSERVER_DATA server, char* pin) {
// brls::Logger::info("Client: PIN: {}, salt {}", pin, salt.hex().bytes());

snprintf(url, sizeof(url),
"http://%s:47989/"
"http://%s:%u/"
"pair?uniqueid=%s&devicename=roth&updateState=1&phrase="
"getservercert&salt=%s&clientcert=%s",
server->serverInfo.address, unique_id.c_str(), salt.hex().bytes(),
server->serverInfo.address,
server->httpPort,
unique_id.c_str(), salt.hex().bytes(),
CryptoManager::cert_data().hex().bytes());

if ((ret = http_request(url, &data, HTTPRequestTimeoutLong)) != GS_OK) {
Expand Down Expand Up @@ -262,9 +288,11 @@ int gs_pair(PSERVER_DATA server, char* pin) {

snprintf(
url, sizeof(url),
"http://%s:47989/"
"http://%s:%u/"
"pair?uniqueid=%s&devicename=roth&updateState=1&clientchallenge=%s",
server->serverInfo.address, unique_id.c_str(),
server->serverInfo.address,
server->httpPort,
unique_id.c_str(),
encryptedChallenge.hex().bytes());

if ((ret = http_request(url, &data, HTTPRequestTimeoutLong)) != GS_OK) {
Expand Down Expand Up @@ -309,9 +337,11 @@ int gs_pair(PSERVER_DATA server, char* pin) {

snprintf(
url, sizeof(url),
"http://%s:47989/"
"http://%s:%u/"
"pair?uniqueid=%s&devicename=roth&updateState=1&serverchallengeresp=%s",
server->serverInfo.address, unique_id.c_str(),
server->serverInfo.address,
server->httpPort,
unique_id.c_str(),
challengeRespEncrypted.hex().bytes());

if ((ret = http_request(url, &data, HTTPRequestTimeoutLong)) != GS_OK) {
Expand Down Expand Up @@ -360,9 +390,11 @@ int gs_pair(PSERVER_DATA server, char* pin) {

snprintf(
url, sizeof(url),
"http://%s:47989/"
"http://%s:%u/"
"pair?uniqueid=%s&devicename=roth&updateState=1&clientpairingsecret=%s",
server->serverInfo.address, unique_id.c_str(),
server->serverInfo.address,
server->httpPort,
unique_id.c_str(),
clientPairingSecret.hex().bytes());
if ((ret = http_request(url, &data, HTTPRequestTimeoutLong)) != GS_OK) {
return gs_pair_cleanup(ret, server, &result);
Expand Down Expand Up @@ -517,7 +549,22 @@ int gs_quit_app(PSERVER_DATA server) {
return ret;
}

int gs_init(PSERVER_DATA server, const std::string address, bool skip_https) {
int gs_init(PSERVER_DATA server, const std::string address) {
std::stringstream addressStream(address);
std::string segment;
std::vector<std::string> seglist;
unsigned short httpPort = 47989; // Default HTTP port

while(std::getline(addressStream, segment, ':'))
{
seglist.push_back(segment);
}

// Override port if it presented
if (seglist.size() > 1) {
httpPort = atoi(seglist[1].c_str());
}

if (!CryptoManager::load_cert_key_pair()) {
brls::Logger::info("Client: No certs, generate new...");

Expand All @@ -530,10 +577,12 @@ int gs_init(PSERVER_DATA server, const std::string address, bool skip_https) {
http_init(Settings::instance().key_dir());

LiInitializeServerInformation(&server->serverInfo);
server->address = address;
server->address = seglist[0];
server->serverInfo.address = server->address.c_str();
server->httpPort = httpPort;
server->httpsPort = 0; /* Populated by load_server_status() */

int result = load_server_status(server, skip_https);
int result = load_server_status(server);
server->serverInfo.serverInfoAppVersion =
server->serverInfoAppVersion.c_str();
server->serverInfo.serverInfoGfeVersion =
Expand Down
8 changes: 4 additions & 4 deletions app/src/libgamestream/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ typedef struct _SERVER_DATA {
std::string gsVersion;
std::string hostname;
SERVER_INFORMATION serverInfo;
unsigned short httpPort;
unsigned short httpsPort;
} SERVER_DATA, *PSERVER_DATA;

void gs_set_error(std::string error);
std::string gs_error();

int gs_init(PSERVER_DATA server, const std::string address,
bool skip_https = false);
int gs_init(PSERVER_DATA server, const std::string address);
int gs_app_boxart(PSERVER_DATA server, int app_id, Data* out);
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId,
bool sops, bool localaudio, int gamepad_mask);
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio, int gamepad_mask);
int gs_applist(PSERVER_DATA server, PAPP_LIST* app_list);
int gs_unpair(PSERVER_DATA server);
int gs_pair(PSERVER_DATA server, char* pin);
Expand Down
2 changes: 1 addition & 1 deletion app/src/streaming/DiscoverManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void DiscoverManager::loop() {
while (counter < addresses.size() && !paused) {
SERVER_DATA server_data;

int status = gs_init(&server_data, addresses[counter], true);
int status = gs_init(&server_data, addresses[counter]);
if (status == GS_OK) {
Host host;
host.address = addresses[counter];
Expand Down
Loading

0 comments on commit df8e858

Please sign in to comment.