1 /*
2  * This file is part of Moonlight Embedded.
3  *
4  * Copyright (C) 2015-2017 Iwan Timmer
5  *
6  * Moonlight is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Moonlight is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "http.h"
21 #include "xml.h"
22 #include "mkcert.h"
23 #include "client.h"
24 #include "errors.h"
25 #include "limits.h"
26 
27 #include <Limelight.h>
28 
29 #include <sys/stat.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <uuid/uuid.h>
34 #include <openssl/sha.h>
35 #include <openssl/aes.h>
36 #include <openssl/rand.h>
37 #include <openssl/evp.h>
38 #include <openssl/x509.h>
39 #include <openssl/pem.h>
40 #include <openssl/err.h>
41 
42 #define UNIQUE_FILE_NAME "uniqueid.dat"
43 #define P12_FILE_NAME "client.p12"
44 
45 #define UNIQUEID_BYTES 8
46 #define UNIQUEID_CHARS (UNIQUEID_BYTES*2)
47 
48 #define CHANNEL_COUNT_STEREO 2
49 #define CHANNEL_COUNT_51_SURROUND 6
50 
51 #define CHANNEL_MASK_STEREO 0x3
52 #define CHANNEL_MASK_51_SURROUND 0xFC
53 
54 static char unique_id[UNIQUEID_CHARS+1];
55 static X509 *cert;
56 static char cert_hex[4096];
57 static EVP_PKEY *privateKey;
58 
59 const char* gs_error;
60 
mkdirtree(const char * directory)61 static int mkdirtree(const char* directory) {
62   char buffer[PATH_MAX];
63   char* p = buffer;
64 
65   // The passed in string could be a string literal
66   // so we must copy it first
67   strncpy(p, directory, PATH_MAX - 1);
68   buffer[PATH_MAX - 1] = '\0';
69 
70   while (*p != 0) {
71     // Find the end of the path element
72     do {
73       p++;
74     } while (*p != 0 && *p != '/');
75 
76     char oldChar = *p;
77     *p = 0;
78 
79     // Create the directory if it doesn't exist already
80     if (mkdir(buffer, 0775) == -1 && errno != EEXIST) {
81         return -1;
82     }
83 
84     *p = oldChar;
85   }
86 
87   return 0;
88 }
89 
load_unique_id(const char * keyDirectory)90 static int load_unique_id(const char* keyDirectory) {
91   char uniqueFilePath[PATH_MAX];
92   snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
93 
94   FILE *fd = fopen(uniqueFilePath, "r");
95   if (fd == NULL) {
96     unsigned char unique_data[UNIQUEID_BYTES];
97     RAND_bytes(unique_data, UNIQUEID_BYTES);
98     for (int i = 0; i < UNIQUEID_BYTES; i++) {
99       sprintf(unique_id + (i * 2), "%02x", unique_data[i]);
100     }
101     fd = fopen(uniqueFilePath, "w");
102     if (fd == NULL)
103       return GS_FAILED;
104 
105     fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
106   } else {
107     fread(unique_id, UNIQUEID_CHARS, 1, fd);
108   }
109   fclose(fd);
110   unique_id[UNIQUEID_CHARS] = 0;
111 
112   return GS_OK;
113 }
114 
load_cert(const char * keyDirectory)115 static int load_cert(const char* keyDirectory) {
116   char certificateFilePath[PATH_MAX];
117   snprintf(certificateFilePath, PATH_MAX, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
118 
119   char keyFilePath[PATH_MAX];
120   snprintf(&keyFilePath[0], PATH_MAX, "%s/%s", keyDirectory, KEY_FILE_NAME);
121 
122   FILE *fd = fopen(certificateFilePath, "r");
123   if (fd == NULL) {
124     printf("Generating certificate...");
125     CERT_KEY_PAIR cert = mkcert_generate();
126     printf("done\n");
127 
128     char p12FilePath[PATH_MAX];
129     snprintf(p12FilePath, PATH_MAX, "%s/%s", keyDirectory, P12_FILE_NAME);
130 
131     mkcert_save(certificateFilePath, p12FilePath, keyFilePath, cert);
132     mkcert_free(cert);
133     fd = fopen(certificateFilePath, "r");
134   }
135 
136   if (fd == NULL) {
137     gs_error = "Can't open certificate file";
138     return GS_FAILED;
139   }
140 
141   if (!(cert = PEM_read_X509(fd, NULL, NULL, NULL))) {
142     gs_error = "Error loading cert into memory";
143     return GS_FAILED;
144   }
145 
146   rewind(fd);
147 
148   int c;
149   int length = 0;
150   while ((c = fgetc(fd)) != EOF) {
151     sprintf(cert_hex + length, "%02x", c);
152     length += 2;
153   }
154   cert_hex[length] = 0;
155 
156   fclose(fd);
157 
158   fd = fopen(keyFilePath, "r");
159   if (fd == NULL) {
160     gs_error = "Error loading key into memory";
161     return GS_FAILED;
162   }
163 
164   PEM_read_PrivateKey(fd, &privateKey, NULL, NULL);
165   fclose(fd);
166 
167   return GS_OK;
168 }
169 
load_server_status(PSERVER_DATA server)170 static int load_server_status(PSERVER_DATA server) {
171 
172   uuid_t uuid;
173   char uuid_str[37];
174 
175   int ret;
176   char url[4096];
177   int i;
178 
179   i = 0;
180   do {
181     char *pairedText = NULL;
182     char *currentGameText = NULL;
183     char *stateText = NULL;
184     char *serverCodecModeSupportText = NULL;
185 
186     ret = GS_INVALID;
187 
188     uuid_generate_random(uuid);
189     uuid_unparse(uuid, uuid_str);
190 
191     // Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
192     // is not already paired. Since we can't pair without knowing the server version, we
193     // make another request over HTTP if the HTTPS request fails. We can't just use HTTP
194     // for everything because it doesn't accurately tell us if we're paired.
195     snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
196       i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? 47984 : 47989, unique_id, uuid_str);
197 
198     PHTTP_DATA data = http_create_data();
199     if (data == NULL) {
200       ret = GS_OUT_OF_MEMORY;
201       goto cleanup;
202     }
203     if (http_request(url, data) != GS_OK) {
204       ret = GS_IO_ERROR;
205       goto cleanup;
206     }
207 
208     if (xml_status(data->memory, data->size) == GS_ERROR) {
209       ret = GS_ERROR;
210       goto cleanup;
211     }
212 
213     if (xml_search(data->memory, data->size, "currentgame", &currentGameText) != GS_OK) {
214       goto cleanup;
215     }
216 
217     if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
218       goto cleanup;
219 
220     if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
221       goto cleanup;
222 
223     if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
224       goto cleanup;
225 
226     if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
227       goto cleanup;
228 
229     if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
230       goto cleanup;
231 
232     if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
233       goto cleanup;
234 
235     if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
236       goto cleanup;
237 
238     if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
239       goto cleanup;
240 
241     // These fields are present on all version of GFE that this client supports
242     if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
243       goto cleanup;
244 
245     server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
246     server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
247     server->supports4K = serverCodecModeSupportText != NULL;
248     server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
249 
250     if (strstr(stateText, "_SERVER_BUSY") == NULL) {
251       // After GFE 2.8, current game remains set even after streaming
252       // has ended. We emulate the old behavior by forcing it to zero
253       // if streaming is not active.
254       server->currentGame = 0;
255     }
256     ret = GS_OK;
257 
258     cleanup:
259     if (data != NULL)
260       http_free_data(data);
261 
262     if (pairedText != NULL)
263       free(pairedText);
264 
265     if (currentGameText != NULL)
266       free(currentGameText);
267 
268     if (serverCodecModeSupportText != NULL)
269       free(serverCodecModeSupportText);
270 
271     i++;
272   } while (ret != GS_OK && i < 2);
273 
274   if (ret == GS_OK && !server->unsupported) {
275     if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
276       gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again";
277       ret = GS_UNSUPPORTED_VERSION;
278     } else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) {
279       gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again.";
280       ret = GS_UNSUPPORTED_VERSION;
281     }
282   }
283 
284   return ret;
285 }
286 
bytes_to_hex(unsigned char * in,char * out,size_t len)287 static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
288   for (int i = 0; i < len; i++) {
289     sprintf(out + i * 2, "%02x", in[i]);
290   }
291   out[len * 2] = 0;
292 }
293 
sign_it(const char * msg,size_t mlen,unsigned char ** sig,size_t * slen,EVP_PKEY * pkey)294 static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *slen, EVP_PKEY *pkey) {
295   int result = GS_FAILED;
296 
297   *sig = NULL;
298   *slen = 0;
299 
300   EVP_MD_CTX *ctx = EVP_MD_CTX_create();
301   if (ctx == NULL)
302     return GS_FAILED;
303 
304   const EVP_MD *md = EVP_get_digestbyname("SHA256");
305   if (md == NULL)
306     goto cleanup;
307 
308   int rc = EVP_DigestInit_ex(ctx, md, NULL);
309   if (rc != 1)
310     goto cleanup;
311 
312   rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
313   if (rc != 1)
314     goto cleanup;
315 
316   rc = EVP_DigestSignUpdate(ctx, msg, mlen);
317   if (rc != 1)
318     goto cleanup;
319 
320   size_t req = 0;
321   rc = EVP_DigestSignFinal(ctx, NULL, &req);
322   if (rc != 1 || !(req > 0))
323     goto cleanup;
324 
325   *sig = OPENSSL_malloc(req);
326   if (*sig == NULL)
327     goto cleanup;
328 
329   *slen = req;
330   rc = EVP_DigestSignFinal(ctx, *sig, slen);
331   if (rc != 1 || req != *slen)
332     goto cleanup;
333 
334   result = GS_OK;
335 
336   cleanup:
337   EVP_MD_CTX_destroy(ctx);
338   ctx = NULL;
339 
340   return result;
341 }
342 
verifySignature(const char * data,int dataLength,char * signature,int signatureLength,const char * cert)343 static bool verifySignature(const char *data, int dataLength, char *signature, int signatureLength, const char *cert) {
344     X509* x509;
345     BIO* bio = BIO_new(BIO_s_mem());
346     BIO_puts(bio, cert);
347     x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
348 
349     BIO_free(bio);
350 
351     if (!x509) {
352         return false;
353     }
354 
355     EVP_PKEY* pubKey = X509_get_pubkey(x509);
356     EVP_MD_CTX *mdctx = NULL;
357     mdctx = EVP_MD_CTX_create();
358     EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubKey);
359     EVP_DigestVerifyUpdate(mdctx, data, dataLength);
360     int result = EVP_DigestVerifyFinal(mdctx, signature, signatureLength);
361 
362     X509_free(x509);
363     EVP_PKEY_free(pubKey);
364     EVP_MD_CTX_destroy(mdctx);
365 
366     return result > 0;
367 }
368 
gs_unpair(PSERVER_DATA server)369 int gs_unpair(PSERVER_DATA server) {
370   int ret = GS_OK;
371   char url[4096];
372   uuid_t uuid;
373   char uuid_str[37];
374   PHTTP_DATA data = http_create_data();
375   if (data == NULL)
376     return GS_OUT_OF_MEMORY;
377 
378   uuid_generate_random(uuid);
379   uuid_unparse(uuid, uuid_str);
380   snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
381   ret = http_request(url, data);
382 
383   http_free_data(data);
384   return ret;
385 }
386 
gs_pair(PSERVER_DATA server,char * pin)387 int gs_pair(PSERVER_DATA server, char* pin) {
388   int ret = GS_OK;
389   char* result = NULL;
390   char url[4096];
391   uuid_t uuid;
392   char uuid_str[37];
393 
394   if (server->paired) {
395     gs_error = "Already paired";
396     return GS_WRONG_STATE;
397   }
398 
399   if (server->currentGame != 0) {
400     gs_error = "The computer is currently in a game. You must close the game before pairing";
401     return GS_WRONG_STATE;
402   }
403 
404   unsigned char salt_data[16];
405   char salt_hex[33];
406   RAND_bytes(salt_data, 16);
407   bytes_to_hex(salt_data, salt_hex, 16);
408 
409   uuid_generate_random(uuid);
410   uuid_unparse(uuid, uuid_str);
411   snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, unique_id, uuid_str, salt_hex, cert_hex);
412   PHTTP_DATA data = http_create_data();
413   if (data == NULL)
414     return GS_OUT_OF_MEMORY;
415   else if ((ret = http_request(url, data)) != GS_OK)
416     goto cleanup;
417 
418   if ((ret = xml_status(data->memory, data->size) != GS_OK))
419     goto cleanup;
420   else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
421     goto cleanup;
422 
423   if (strcmp(result, "1") != 0) {
424     gs_error = "Pairing failed";
425     ret = GS_FAILED;
426     goto cleanup;
427   }
428 
429   free(result);
430   result = NULL;
431   if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK)
432     goto cleanup;
433 
434   if (strlen(result)/2 > 8191) {
435     gs_error = "Server certificate too big";
436     ret = GS_FAILED;
437     goto cleanup;
438   }
439 
440   char plaincert[8192];
441   for (int count = 0; count < strlen(result); count += 2) {
442     sscanf(&result[count], "%2hhx", &plaincert[count / 2]);
443   }
444   plaincert[strlen(result)/2] = '\0';
445 
446   unsigned char salt_pin[20];
447   unsigned char aes_key_hash[32];
448   AES_KEY enc_key, dec_key;
449   memcpy(salt_pin, salt_data, 16);
450   memcpy(salt_pin+16, pin, 4);
451 
452   int hash_length = server->serverMajorVersion >= 7 ? 32 : 20;
453   if (server->serverMajorVersion >= 7)
454     SHA256(salt_pin, 20, aes_key_hash);
455   else
456     SHA1(salt_pin, 20, aes_key_hash);
457 
458   AES_set_encrypt_key((unsigned char *)aes_key_hash, 128, &enc_key);
459   AES_set_decrypt_key((unsigned char *)aes_key_hash, 128, &dec_key);
460 
461   unsigned char challenge_data[16];
462   unsigned char challenge_enc[16];
463   char challenge_hex[33];
464   RAND_bytes(challenge_data, 16);
465   AES_encrypt(challenge_data, challenge_enc, &enc_key);
466   bytes_to_hex(challenge_enc, challenge_hex, 16);
467 
468   uuid_generate_random(uuid);
469   uuid_unparse(uuid, uuid_str);
470   snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, unique_id, uuid_str, challenge_hex);
471   if ((ret = http_request(url, data)) != GS_OK)
472     goto cleanup;
473 
474   free(result);
475   result = NULL;
476   if ((ret = xml_status(data->memory, data->size) != GS_OK))
477     goto cleanup;
478   else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
479     goto cleanup;
480 
481   if (strcmp(result, "1") != 0) {
482     gs_error = "Pairing failed";
483     ret = GS_FAILED;
484     goto cleanup;
485   }
486 
487   free(result);
488   result = NULL;
489   if (xml_search(data->memory, data->size, "challengeresponse", &result) != GS_OK) {
490     ret = GS_INVALID;
491     goto cleanup;
492   }
493 
494   char challenge_response_data_enc[48];
495   char challenge_response_data[48];
496   for (int count = 0; count < strlen(result); count += 2) {
497     sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]);
498   }
499 
500   for (int i = 0; i < 48; i += 16) {
501     AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &dec_key);
502   }
503 
504   char client_secret_data[16];
505   RAND_bytes(client_secret_data, 16);
506 
507   const ASN1_BIT_STRING *asnSignature;
508 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
509   X509_get0_signature(&asnSignature, NULL, cert);
510 #else
511   asnSignature = cert->signature;
512 #endif
513 
514   char challenge_response[16 + 256 + 16];
515   char challenge_response_hash[32];
516   char challenge_response_hash_enc[32];
517   char challenge_response_hex[65];
518   memcpy(challenge_response, challenge_response_data + hash_length, 16);
519   memcpy(challenge_response + 16, asnSignature->data, 256);
520   memcpy(challenge_response + 16 + 256, client_secret_data, 16);
521   if (server->serverMajorVersion >= 7)
522     SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash);
523   else
524     SHA1(challenge_response, 16 + 256 + 16, challenge_response_hash);
525 
526   for (int i = 0; i < 32; i += 16) {
527     AES_encrypt(&challenge_response_hash[i], &challenge_response_hash_enc[i], &enc_key);
528   }
529   bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, 32);
530 
531   uuid_generate_random(uuid);
532   uuid_unparse(uuid, uuid_str);
533   snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, unique_id, uuid_str, challenge_response_hex);
534   if ((ret = http_request(url, data)) != GS_OK)
535     goto cleanup;
536 
537   free(result);
538   result = NULL;
539   if ((ret = xml_status(data->memory, data->size) != GS_OK))
540     goto cleanup;
541   else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
542     goto cleanup;
543 
544   if (strcmp(result, "1") != 0) {
545     gs_error = "Pairing failed";
546     ret = GS_FAILED;
547     goto cleanup;
548   }
549 
550   free(result);
551   result = NULL;
552   if (xml_search(data->memory, data->size, "pairingsecret", &result) != GS_OK) {
553     ret = GS_INVALID;
554     goto cleanup;
555   }
556 
557   char pairing_secret[16 + 256];
558   for (int count = 0; count < strlen(result); count += 2) {
559     sscanf(&result[count], "%2hhx", &pairing_secret[count / 2]);
560   }
561 
562   if (!verifySignature(pairing_secret, 16, pairing_secret+16, 256, plaincert)) {
563     gs_error = "MITM attack detected";
564     ret = GS_FAILED;
565     goto cleanup;
566   }
567 
568   unsigned char *signature = NULL;
569   size_t s_len;
570   if (sign_it(client_secret_data, 16, &signature, &s_len, privateKey) != GS_OK) {
571       gs_error = "Failed to sign data";
572       ret = GS_FAILED;
573       goto cleanup;
574   }
575 
576   char client_pairing_secret[16 + 256];
577   char client_pairing_secret_hex[(16 + 256) * 2 + 1];
578   memcpy(client_pairing_secret, client_secret_data, 16);
579   memcpy(client_pairing_secret + 16, signature, 256);
580   bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, 16 + 256);
581 
582   uuid_generate_random(uuid);
583   uuid_unparse(uuid, uuid_str);
584   snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, unique_id, uuid_str, client_pairing_secret_hex);
585   if ((ret = http_request(url, data)) != GS_OK)
586     goto cleanup;
587 
588   free(result);
589   result = NULL;
590   if ((ret = xml_status(data->memory, data->size) != GS_OK))
591     goto cleanup;
592   else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
593     goto cleanup;
594 
595   if (strcmp(result, "1") != 0) {
596     gs_error = "Pairing failed";
597     ret = GS_FAILED;
598     goto cleanup;
599   }
600 
601   uuid_generate_random(uuid);
602   uuid_unparse(uuid, uuid_str);
603   snprintf(url, sizeof(url), "https://%s:47984/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, unique_id, uuid_str);
604   if ((ret = http_request(url, data)) != GS_OK)
605     goto cleanup;
606 
607   free(result);
608   result = NULL;
609   if ((ret = xml_status(data->memory, data->size) != GS_OK))
610     goto cleanup;
611   else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
612     goto cleanup;
613 
614   if (strcmp(result, "1") != 0) {
615     gs_error = "Pairing failed";
616     ret = GS_FAILED;
617     goto cleanup;
618   }
619 
620   server->paired = true;
621 
622   cleanup:
623   if (ret != GS_OK)
624     gs_unpair(server);
625 
626   if (result != NULL)
627     free(result);
628 
629   http_free_data(data);
630 
631   return ret;
632 }
633 
gs_applist(PSERVER_DATA server,PAPP_LIST * list)634 int gs_applist(PSERVER_DATA server, PAPP_LIST *list) {
635   int ret = GS_OK;
636   char url[4096];
637   uuid_t uuid;
638   char uuid_str[37];
639   PHTTP_DATA data = http_create_data();
640   if (data == NULL)
641     return GS_OUT_OF_MEMORY;
642 
643   uuid_generate_random(uuid);
644   uuid_unparse(uuid, uuid_str);
645   snprintf(url, sizeof(url), "https://%s:47984/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
646   if (http_request(url, data) != GS_OK)
647     ret = GS_IO_ERROR;
648   else if (xml_status(data->memory, data->size) == GS_ERROR)
649     ret = GS_ERROR;
650   else if (xml_applist(data->memory, data->size, list) != GS_OK)
651     ret = GS_INVALID;
652 
653   http_free_data(data);
654   return ret;
655 }
656 
gs_start_app(PSERVER_DATA server,STREAM_CONFIGURATION * config,int appId,bool sops,bool localaudio,int gamepad_mask)657 int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops, bool localaudio, int gamepad_mask) {
658   int ret = GS_OK;
659   uuid_t uuid;
660   char* result = NULL;
661   char uuid_str[37];
662 
663   PDISPLAY_MODE mode = server->modes;
664   bool correct_mode = false;
665   while (mode != NULL) {
666     if (mode->width == config->width && mode->height == config->height && mode->refresh == config->fps)
667       correct_mode = true;
668 
669     mode = mode->next;
670   }
671 
672   if (!correct_mode && !server->unsupported)
673     return GS_NOT_SUPPORTED_MODE;
674 
675   if (config->height >= 2160 && !server->supports4K)
676     return GS_NOT_SUPPORTED_4K;
677 
678   RAND_bytes(config->remoteInputAesKey, 16);
679   memset(config->remoteInputAesIv, 0, 16);
680 
681   srand(time(NULL));
682   char url[4096];
683   u_int32_t rikeyid = 0;
684   char rikey_hex[33];
685   bytes_to_hex(config->remoteInputAesKey, rikey_hex, 16);
686 
687   PHTTP_DATA data = http_create_data();
688   if (data == NULL)
689     return GS_OUT_OF_MEMORY;
690 
691   uuid_generate_random(uuid);
692   uuid_unparse(uuid, uuid_str);
693   if (server->currentGame == 0) {
694     int channelCounnt = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_COUNT_STEREO : CHANNEL_COUNT_51_SURROUND;
695     int mask = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_MASK_STEREO : CHANNEL_MASK_51_SURROUND;
696     snprintf(url, sizeof(url), "https://%s:47984/launch?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d", server->serverInfo.address, unique_id, uuid_str, appId, config->width, config->height, config->fps, sops, rikey_hex, rikeyid, localaudio, (mask << 16) + channelCounnt, gamepad_mask, gamepad_mask);
697   } else
698     snprintf(url, sizeof(url), "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d", server->serverInfo.address, unique_id, uuid_str, rikey_hex, rikeyid);
699 
700   if ((ret = http_request(url, data)) == GS_OK)
701     server->currentGame = appId;
702   else
703     goto cleanup;
704 
705   if ((ret = xml_status(data->memory, data->size) != GS_OK))
706     goto cleanup;
707   else if ((ret = xml_search(data->memory, data->size, "gamesession", &result)) != GS_OK)
708     goto cleanup;
709 
710   if (!strcmp(result, "0")) {
711     ret = GS_FAILED;
712     goto cleanup;
713   }
714 
715   cleanup:
716   if (result != NULL)
717     free(result);
718 
719   http_free_data(data);
720   return ret;
721 }
722 
gs_quit_app(PSERVER_DATA server)723 int gs_quit_app(PSERVER_DATA server) {
724   int ret = GS_OK;
725   char url[4096];
726   uuid_t uuid;
727   char uuid_str[37];
728   char* result = NULL;
729   PHTTP_DATA data = http_create_data();
730   if (data == NULL)
731     return GS_OUT_OF_MEMORY;
732 
733   uuid_generate_random(uuid);
734   uuid_unparse(uuid, uuid_str);
735   snprintf(url, sizeof(url), "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
736   if ((ret = http_request(url, data)) != GS_OK)
737     goto cleanup;
738 
739   if ((ret = xml_status(data->memory, data->size) != GS_OK))
740     goto cleanup;
741   else if ((ret = xml_search(data->memory, data->size, "cancel", &result)) != GS_OK)
742     goto cleanup;
743 
744   if (strcmp(result, "0") == 0) {
745     ret = GS_FAILED;
746     goto cleanup;
747   }
748 
749   cleanup:
750   if (result != NULL)
751     free(result);
752 
753   http_free_data(data);
754   return ret;
755 }
756 
gs_init(PSERVER_DATA server,char * address,const char * keyDirectory,int log_level,bool unsupported)757 int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
758   mkdirtree(keyDirectory);
759   if (load_unique_id(keyDirectory) != GS_OK)
760     return GS_FAILED;
761 
762   if (load_cert(keyDirectory))
763     return GS_FAILED;
764 
765   http_init(keyDirectory, log_level);
766 
767   LiInitializeServerInformation(&server->serverInfo);
768   server->serverInfo.address = address;
769   server->unsupported = unsupported;
770   return load_server_status(server);
771 }
772