Add support for custom ports with Sunshine

This commit is contained in:
Cameron Gutman 2022-09-23 22:48:43 -05:00
parent 3f00f25a39
commit 0325a3b88c
7 changed files with 147 additions and 110 deletions

View File

@ -166,33 +166,22 @@ static int load_cert(const char* keyDirectory) {
return GS_OK; return GS_OK;
} }
static int load_server_status(PSERVER_DATA server) { static int load_serverinfo(PSERVER_DATA server, bool https) {
uuid_t uuid; uuid_t uuid;
char uuid_str[UUID_STRLEN]; char uuid_str[UUID_STRLEN];
int ret;
char url[4096]; char url[4096];
int i; int ret = GS_INVALID;
i = 0;
do {
char *pairedText = NULL; char *pairedText = NULL;
char *currentGameText = NULL; char *currentGameText = NULL;
char *stateText = NULL; char *stateText = NULL;
char *serverCodecModeSupportText = NULL; char *serverCodecModeSupportText = NULL;
char *httpsPortText = NULL;
ret = GS_INVALID;
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_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.
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s", snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? server->httpsPort : server->httpPort, unique_id, uuid_str); https ? "https" : "http", server->serverInfo.address, https ? server->httpsPort : server->httpPort, unique_id, uuid_str);
PHTTP_DATA data = http_create_data(); PHTTP_DATA data = http_create_data();
if (data == NULL) { if (data == NULL) {
@ -234,6 +223,9 @@ static int load_server_status(PSERVER_DATA server) {
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK) if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
goto cleanup; goto cleanup;
if (xml_search(data->memory, data->size, "HttpsPort", &httpsPortText) != GS_OK)
goto cleanup;
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK) if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
goto cleanup; goto cleanup;
@ -246,6 +238,10 @@ static int load_server_status(PSERVER_DATA server) {
server->supports4K = serverCodecModeSupportText != NULL; server->supports4K = serverCodecModeSupportText != NULL;
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion); server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
server->httpsPort = atoi(httpsPortText);
if (!server->httpsPort)
server->httpsPort = 47984;
if (strstr(stateText, "_SERVER_BUSY") == NULL) { if (strstr(stateText, "_SERVER_BUSY") == NULL) {
// After GFE 2.8, current game remains set even after streaming // After GFE 2.8, current game remains set even after streaming
// has ended. We emulate the old behavior by forcing it to zero // has ended. We emulate the old behavior by forcing it to zero
@ -267,8 +263,31 @@ static int load_server_status(PSERVER_DATA server) {
if (serverCodecModeSupportText != NULL) if (serverCodecModeSupportText != NULL)
free(serverCodecModeSupportText); free(serverCodecModeSupportText);
i++; if (httpsPortText != NULL)
} while (ret != GS_OK && i < 2); free(httpsPortText);
return ret;
}
static int load_server_status(PSERVER_DATA server) {
int ret;
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;
}
// 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 && !server->unsupported) { if (ret == GS_OK && !server->unsupported) {
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) { if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
@ -790,7 +809,9 @@ int gs_quit_app(PSERVER_DATA server) {
return ret; return ret;
} }
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) { int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const char *keyDirectory, int log_level, bool unsupported) {
char* portSeparator;
mkdirtree(keyDirectory); mkdirtree(keyDirectory);
if (load_unique_id(keyDirectory) != GS_OK) if (load_unique_id(keyDirectory) != GS_OK)
return GS_FAILED; return GS_FAILED;
@ -803,7 +824,7 @@ int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int lo
LiInitializeServerInformation(&server->serverInfo); LiInitializeServerInformation(&server->serverInfo);
server->serverInfo.address = address; server->serverInfo.address = address;
server->unsupported = unsupported; server->unsupported = unsupported;
server->httpPort = 47989; server->httpPort = httpPort ? httpPort : 47989;
server->httpsPort = 47984; server->httpsPort = 0; /* Populated by load_server_status() */
return load_server_status(server); return load_server_status(server);
} }

View File

@ -42,7 +42,7 @@ typedef struct _SERVER_DATA {
unsigned short httpsPort; unsigned short httpsPort;
} SERVER_DATA, *PSERVER_DATA; } SERVER_DATA, *PSERVER_DATA;
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported); int gs_init(PSERVER_DATA server, char* address, unsigned short httpPort, const char *keyDirectory, int logLevel, bool unsupported);
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_applist(PSERVER_DATA server, PAPP_LIST *app_list);
int gs_unpair(PSERVER_DATA server); int gs_unpair(PSERVER_DATA server);

View File

@ -33,6 +33,11 @@
static AvahiSimplePoll *simple_poll = NULL; static AvahiSimplePoll *simple_poll = NULL;
struct cb_ctx {
char* address;
unsigned short* port;
};
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
if (state == AVAHI_CLIENT_FAILURE) { if (state == AVAHI_CLIENT_FAILURE) {
gs_error = "Server connection failure"; gs_error = "Server connection failure";
@ -43,12 +48,14 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) {
if (event == AVAHI_RESOLVER_FOUND) { if (event == AVAHI_RESOLVER_FOUND) {
if (userdata != NULL) { if (userdata != NULL) {
avahi_address_snprint(userdata, AVAHI_ADDRESS_STR_MAX, address); struct cb_ctx* ctx = userdata;
avahi_address_snprint(ctx->address, AVAHI_ADDRESS_STR_MAX, address);
*ctx->port = port;
avahi_simple_poll_quit(simple_poll); avahi_simple_poll_quit(simple_poll);
} else { } else {
char strAddress[AVAHI_ADDRESS_STR_MAX]; char strAddress[AVAHI_ADDRESS_STR_MAX];
avahi_address_snprint(strAddress, sizeof(strAddress), address); avahi_address_snprint(strAddress, sizeof(strAddress), address);
printf(" %s (%s)\n", host_name, strAddress); printf(" %s (%s:%u)\n", host_name, strAddress, port);
} }
} }
@ -73,7 +80,7 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah
} }
} }
void gs_discover_server(char* dest) { void gs_discover_server(char* dest, unsigned short* port) {
AvahiClient *client = NULL; AvahiClient *client = NULL;
AvahiServiceBrowser *sb = NULL; AvahiServiceBrowser *sb = NULL;
@ -89,7 +96,10 @@ void gs_discover_server(char* dest) {
goto cleanup; goto cleanup;
} }
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, dest))) { struct cb_ctx ctx;
ctx.address = dest;
ctx.port = port;
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, &ctx))) {
gs_error = "Failed to create service browser"; gs_error = "Failed to create service browser";
goto cleanup; goto cleanup;
} }

View File

@ -21,4 +21,4 @@
#define MAX_ADDRESS_SIZE 40 #define MAX_ADDRESS_SIZE 40
void gs_discover_server(char* dest); void gs_discover_server(char* dest, unsigned short* port);

View File

@ -73,6 +73,7 @@ static struct option long_options[] = {
{"debug", no_argument, NULL, 'Z'}, {"debug", no_argument, NULL, 'Z'},
{"nomouseemulation", no_argument, NULL, '4'}, {"nomouseemulation", no_argument, NULL, '4'},
{"pin", required_argument, NULL, '5'}, {"pin", required_argument, NULL, '5'},
{"port", required_argument, NULL, '6'},
{0, 0, 0, 0}, {0, 0, 0, 0},
}; };
@ -248,6 +249,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
case '5': case '5':
config->pin = atoi(value); config->pin = atoi(value);
break; break;
case '6':
config->port = atoi(value);
break;
case 1: case 1:
if (config->action == NULL) if (config->action == NULL)
config->action = value; config->action = value;
@ -369,6 +373,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
config->rotate = 0; config->rotate = 0;
config->codec = CODEC_UNSPECIFIED; config->codec = CODEC_UNSPECIFIED;
config->pin = 0; config->pin = 0;
config->port = 47989;
config->inputsCount = 0; config->inputsCount = 0;
config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS")); config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS"));
@ -386,7 +391,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
} else { } else {
int option_index = 0; int option_index = 0;
int c; int c;
while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:s:tu:v:w:xy45:", long_options, &option_index)) != -1) { while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:s:tu:v:w:xy45:6:", long_options, &option_index)) != -1) {
parse_argument(c, optarg, config); parse_argument(c, optarg, config);
} }
} }

View File

@ -48,6 +48,7 @@ typedef struct _CONFIGURATION {
int inputsCount; int inputsCount;
enum codecs codec; enum codecs codec;
int pin; int pin;
unsigned short port;
} CONFIGURATION, *PCONFIGURATION; } CONFIGURATION, *PCONFIGURATION;
extern bool inputAdded; extern bool inputAdded;

View File

@ -263,7 +263,7 @@ int main(int argc, char* argv[]) {
} }
config.address[0] = 0; config.address[0] = 0;
printf("Searching for server...\n"); printf("Searching for server...\n");
gs_discover_server(config.address); gs_discover_server(config.address, &config.port);
if (config.address[0] == 0) { if (config.address[0] == 0) {
fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n"); fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n");
exit(-1); exit(-1);
@ -279,7 +279,7 @@ int main(int argc, char* argv[]) {
printf("Connecting to %s...\n", config.address); printf("Connecting to %s...\n", config.address);
int ret; int ret;
if ((ret = gs_init(&server, config.address, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) { if ((ret = gs_init(&server, config.address, config.port, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
fprintf(stderr, "Not enough memory\n"); fprintf(stderr, "Not enough memory\n");
exit(-1); exit(-1);
} else if (ret == GS_ERROR) { } else if (ret == GS_ERROR) {