mirror of
https://github.com/rock88/moonlight-nx
synced 2024-11-25 21:10:23 +00:00
Merge branch 'libnx' into libnx-new
* libnx: WIP (not work) Try to run this on a Horizon OS :C # Conflicts: # src/GameStreamClient.cpp # src/GameStreamClient.hpp # src/Settings.cpp # src/decoders/gl_render.c # src/moonlight_glfw.cpp
This commit is contained in:
commit
fe905ae1a2
25 changed files with 1613 additions and 386 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -7,3 +7,6 @@
|
|||
[submodule "third_party/json"]
|
||||
path = third_party/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "third_party/libuuid"]
|
||||
path = third_party/libuuid
|
||||
url = https://github.com/fadedreamz/libuuid.git
|
||||
|
|
2
Makefile
2
Makefile
|
@ -206,7 +206,7 @@ DEFINES += -DNANOGUI_USE_OPENGL -DNVG_STB_IMAGE_IMPLEMENTATION -DNANOGUI_NO_GLFW
|
|||
-DHAS_SOCKLEN_T -DHAS_POLL -DHAS_FCNTL -D_GNU_SOURCE -D__LIBRETRO__
|
||||
|
||||
CFLAGS += -Wall -pedantic $(fpic) -std=gnu11 $(DEFINES)
|
||||
CXXFLAGS += -std=gnu++17 -fno-permissive $(DEFINES)
|
||||
CXXFLAGS += -std=gnu++17 $(DEFINES)
|
||||
|
||||
LIBS += -lcrypto -lssl -lcurl -lz -lexpat -lopus \
|
||||
-lavcodec -lavformat -lavutil -lavdevice -lstdc++ -lswresample
|
||||
|
|
341
Makefile.libnx
Normal file
341
Makefile.libnx
Normal file
|
@ -0,0 +1,341 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||
#
|
||||
# NO_ICON: if set to anything, do not use icon.
|
||||
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.jpg
|
||||
# - icon.jpg
|
||||
# - <libnx folder>/default_icon.jpg
|
||||
#
|
||||
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.json
|
||||
# - config.json
|
||||
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := moonlight
|
||||
BUILD := build
|
||||
SOURCES := libgamestream src/nanogui_resources src src/streaming src/streaming/ffmpeg src/streaming/video src/ui/windows src/ui/buttons src/ui \
|
||||
third_party/moonlight-common-c/enet third_party/moonlight-common-c/reedsolomon third_party/moonlight-common-c/src \
|
||||
third_party/nanogui/ext/nanovg/src third_party/nanogui/src
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
#ROMFS := romfs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
M_INCLUDES := \
|
||||
-I$(TOPDIR)/src -I$(TOPDIR)/src/streaming \
|
||||
-I$(TOPDIR)/src/streaming/ffmpeg -I$(TOPDIR)/src/streaming/video -I$(TOPDIR)/src/streaming/audio \
|
||||
-I$(TOPDIR)/src/nanogui_resources \
|
||||
-I$(TOPDIR)/src/ui -I$(TOPDIR)/src/ui/buttons -I$(TOPDIR)/src/ui/windows \
|
||||
-I$(TOPDIR)/libgamestream \
|
||||
-I$(TOPDIR)/third_party/moonlight-common-c/reedsolomon \
|
||||
-I$(TOPDIR)/third_party/moonlight-common-c/src \
|
||||
-I$(TOPDIR)/third_party/moonlight-common-c/enet/include \
|
||||
-I$(TOPDIR)/third_party/nanogui/include \
|
||||
-I$(TOPDIR)/third_party/nanogui/ext/nanovg/src \
|
||||
-I$(TOPDIR)/third_party/json/single_include/nlohmann
|
||||
|
||||
DEFINES := -DNANOGUI_USE_OPENGL -DNVG_STB_IMAGE_IMPLEMENTATION -DNANOGUI_NO_GLFW \
|
||||
-DHAS_SOCKLEN_T -DHAS_POLL -DHAS_FCNTL -D_GNU_SOURCE
|
||||
|
||||
CFLAGS := -g -Wall -O0 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) $(M_INCLUDES) -D__SWITCH__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++17
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto \
|
||||
-lavcodec -lavutil -lopus -lssl -lcrypto -lbz2 -lz -lexpat -lm \
|
||||
-lglad -lEGL -lglapi -ldrm_nouveau -lglfw3 \
|
||||
-lnx -lwebp -lswresample -lavformat -lvpx \
|
||||
-L/Users/rock88/Documents/Projects/Switch/moonlight-switch-new/deps/lib
|
||||
|
||||
LIBGAMESTREAM_SOURCES = \
|
||||
client.c \
|
||||
http.c \
|
||||
mkcert.c \
|
||||
xml.c
|
||||
|
||||
MOONLIGHT_LIBRETRO_C_SOURCES = \
|
||||
nanogui_resources.c \
|
||||
moonlight_libnx.c \
|
||||
moonlight_glfw.c
|
||||
|
||||
MOONLIGHT_LIBRETRO_CXX_SOURCES = \
|
||||
AddHostWindow.cpp \
|
||||
ContentWindow.cpp \
|
||||
MainWindow.cpp \
|
||||
StreamWindow.cpp \
|
||||
AppListWindow.cpp \
|
||||
SettingsWindow.cpp \
|
||||
AddHostButton.cpp \
|
||||
AppButton.cpp \
|
||||
HostButton.cpp \
|
||||
Application.cpp \
|
||||
LoadingOverlay.cpp \
|
||||
GameStreamClient.cpp \
|
||||
Settings.cpp \
|
||||
InputController.cpp \
|
||||
MoonlightSession.cpp \
|
||||
FFmpegVideoDecoder.cpp \
|
||||
GLVideoRenderer.cpp
|
||||
|
||||
MOONLIGHT_COMMON_C_SOURCES = \
|
||||
callbacks.c \
|
||||
compress.c \
|
||||
host.c \
|
||||
list.c \
|
||||
packet.c \
|
||||
peer.c \
|
||||
protocol.c \
|
||||
unix.c \
|
||||
win32.c \
|
||||
rs.c \
|
||||
AudioStream.c \
|
||||
ByteBuffer.c \
|
||||
Connection.c \
|
||||
ControlStream.c \
|
||||
FakeCallbacks.c \
|
||||
InputStream.c \
|
||||
LinkedBlockingQueue.c \
|
||||
Misc.c \
|
||||
Platform.c \
|
||||
PlatformSockets.c \
|
||||
RtpFecQueue.c \
|
||||
RtpReorderQueue.c \
|
||||
RtspConnection.c \
|
||||
RtspParser.c \
|
||||
SdpGenerator.c \
|
||||
SimpleStun.c \
|
||||
VideoDepacketizer.c \
|
||||
VideoStream.c
|
||||
|
||||
NANOGUI_C_SOURCES = \
|
||||
nanovg.c
|
||||
|
||||
NANOGUI_CXX_SOURCES = \
|
||||
widget.cpp \
|
||||
button.cpp \
|
||||
common.cpp \
|
||||
screen.cpp \
|
||||
checkbox.cpp \
|
||||
vscrollpanel.cpp \
|
||||
colorpicker.cpp \
|
||||
textarea.cpp \
|
||||
shader_gl.cpp \
|
||||
canvas.cpp \
|
||||
window.cpp \
|
||||
graph.cpp \
|
||||
popup.cpp \
|
||||
layout.cpp \
|
||||
texture.cpp \
|
||||
texture_gl.cpp \
|
||||
tabwidget.cpp \
|
||||
shader.cpp \
|
||||
imageview.cpp \
|
||||
progressbar.cpp \
|
||||
combobox.cpp \
|
||||
theme.cpp \
|
||||
traits.cpp \
|
||||
label.cpp \
|
||||
opengl.cpp \
|
||||
renderpass_gl.cpp \
|
||||
imagepanel.cpp \
|
||||
colorwheel.cpp \
|
||||
messagedialog.cpp \
|
||||
textbox.cpp \
|
||||
slider.cpp \
|
||||
popupbutton.cpp
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(LIBGAMESTREAM_SOURCES) $(MOONLIGHT_LIBRETRO_C_SOURCES) $(MOONLIGHT_COMMON_C_SOURCES) $(NANOGUI_C_SOURCES)
|
||||
CPPFILES := $(MOONLIGHT_LIBRETRO_CXX_SOURCES) $(NANOGUI_CXX_SOURCES)
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(ICON)),)
|
||||
icons := $(wildcard *.jpg)
|
||||
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||
else
|
||||
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_ICON)),)
|
||||
export NROFLAGS += --icon=$(APP_ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||
endif
|
||||
|
||||
ifneq ($(APP_TITLEID),)
|
||||
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||
endif
|
||||
|
||||
ifneq ($(ROMFS),)
|
||||
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.libnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
else
|
||||
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||
endif
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
|
||||
all : $(OUTPUT).nro
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||
else
|
||||
$(OUTPUT).nro : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
endif
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
3
dbg.sh
Executable file
3
dbg.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
/opt/devkitpro/devkitA64/bin/aarch64-none-elf-addr2line -e moonlight.elf -f -p -C -a $1
|
|
@ -30,7 +30,6 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/rand.h>
|
||||
|
@ -38,6 +37,7 @@
|
|||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include "Log.h"
|
||||
|
||||
#define UNIQUE_FILE_NAME "uniqueid.dat"
|
||||
#define P12_FILE_NAME "client.p12"
|
||||
|
@ -58,6 +58,35 @@ static EVP_PKEY *privateKey;
|
|||
|
||||
const char* gs_error;
|
||||
|
||||
typedef unsigned char uuid_t[16];
|
||||
typedef uuid_t uuid;
|
||||
|
||||
uid_t getuid() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void simple_uuid_generate_random(uuid_t out) {
|
||||
static bool once = false;
|
||||
if (!once) {
|
||||
srand(time(NULL));
|
||||
once = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
out[i] = (rand() % 16) * 10 + rand() % 16;
|
||||
}
|
||||
}
|
||||
|
||||
void simple_uuid_unparse(const uuid_t uu, char *out) {
|
||||
sprintf(out, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
|
||||
uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
|
||||
LOG_FMT("out = %s\n", out);
|
||||
}
|
||||
|
||||
#define uuid_generate_random simple_uuid_generate_random
|
||||
#define uuid_unparse simple_uuid_unparse
|
||||
|
||||
int mkdirtree(const char* directory) {
|
||||
char buffer[PATH_MAX];
|
||||
char* p = buffer;
|
||||
|
@ -86,211 +115,211 @@ int mkdirtree(const char* directory) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_unique_id(const char* keyDirectory) {
|
||||
char uniqueFilePath[PATH_MAX];
|
||||
snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
|
||||
|
||||
FILE *fd = fopen(uniqueFilePath, "r");
|
||||
if (fd == NULL) {
|
||||
unsigned char unique_data[UNIQUEID_BYTES];
|
||||
RAND_bytes(unique_data, UNIQUEID_BYTES);
|
||||
for (int i = 0; i < UNIQUEID_BYTES; i++) {
|
||||
sprintf(unique_id + (i * 2), "%02x", unique_data[i]);
|
||||
}
|
||||
fd = fopen(uniqueFilePath, "w");
|
||||
if (fd == NULL)
|
||||
return GS_FAILED;
|
||||
|
||||
fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
} else {
|
||||
fread(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
}
|
||||
fclose(fd);
|
||||
unique_id[UNIQUEID_CHARS] = 0;
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int load_cert(const char* keyDirectory) {
|
||||
char certificateFilePath[PATH_MAX];
|
||||
snprintf(certificateFilePath, PATH_MAX, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
|
||||
char keyFilePath[PATH_MAX];
|
||||
snprintf(&keyFilePath[0], PATH_MAX, "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
|
||||
FILE *fd = fopen(certificateFilePath, "r");
|
||||
if (fd == NULL) {
|
||||
printf("Generating certificate...");
|
||||
CERT_KEY_PAIR cert = mkcert_generate();
|
||||
printf("done\n");
|
||||
|
||||
char p12FilePath[PATH_MAX];
|
||||
snprintf(p12FilePath, PATH_MAX, "%s/%s", keyDirectory, P12_FILE_NAME);
|
||||
|
||||
mkcert_save(certificateFilePath, p12FilePath, keyFilePath, cert);
|
||||
mkcert_free(cert);
|
||||
fd = fopen(certificateFilePath, "r");
|
||||
}
|
||||
|
||||
if (fd == NULL) {
|
||||
gs_error = "Can't open certificate file";
|
||||
return GS_FAILED;
|
||||
}
|
||||
|
||||
if (!(cert = PEM_read_X509(fd, NULL, NULL, NULL))) {
|
||||
gs_error = "Error loading cert into memory";
|
||||
return GS_FAILED;
|
||||
}
|
||||
|
||||
rewind(fd);
|
||||
|
||||
int c;
|
||||
int length = 0;
|
||||
while ((c = fgetc(fd)) != EOF) {
|
||||
sprintf(cert_hex + length, "%02x", c);
|
||||
length += 2;
|
||||
}
|
||||
cert_hex[length] = 0;
|
||||
|
||||
fclose(fd);
|
||||
|
||||
fd = fopen(keyFilePath, "r");
|
||||
if (fd == NULL) {
|
||||
gs_error = "Error loading key into memory";
|
||||
return GS_FAILED;
|
||||
}
|
||||
|
||||
PEM_read_PrivateKey(fd, &privateKey, NULL, NULL);
|
||||
fclose(fd);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int load_server_status(PSERVER_DATA server) {
|
||||
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
|
||||
int ret;
|
||||
char url[4096];
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
|
||||
ret = GS_INVALID;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
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.
|
||||
|
||||
printf("server->serverInfo.address: %s\n", server->serverInfo.address);
|
||||
printf("unique_id: %s\n", unique_id);
|
||||
printf("uuid_str: %s\n", uuid_str);
|
||||
|
||||
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
|
||||
i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? 47984 : 47989, unique_id, uuid_str);
|
||||
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL) {
|
||||
ret = GS_OUT_OF_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
if (http_request(url, data) != GS_OK) {
|
||||
ret = GS_IO_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_status(data->memory, data->size) == GS_ERROR) {
|
||||
ret = GS_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "hostname", &server->hostname) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// These fields are present on all version of GFE that this client supports
|
||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
goto cleanup;
|
||||
|
||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
server->supports4K = serverCodecModeSupportText != NULL;
|
||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
|
||||
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// 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:
|
||||
if (data != NULL)
|
||||
http_free_data(data);
|
||||
|
||||
if (pairedText != NULL)
|
||||
free(pairedText);
|
||||
|
||||
if (currentGameText != NULL)
|
||||
free(currentGameText);
|
||||
|
||||
if (serverCodecModeSupportText != NULL)
|
||||
free(serverCodecModeSupportText);
|
||||
|
||||
i++;
|
||||
} while (ret != GS_OK && i < 2);
|
||||
|
||||
if (ret == GS_OK && !server->unsupported) {
|
||||
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again";
|
||||
ret = GS_UNSUPPORTED_VERSION;
|
||||
} else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again.";
|
||||
ret = GS_UNSUPPORTED_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
//
|
||||
//static int load_unique_id(const char* keyDirectory) {
|
||||
// char uniqueFilePath[PATH_MAX];
|
||||
// snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
|
||||
//
|
||||
// FILE *fd = fopen(uniqueFilePath, "r");
|
||||
// if (fd == NULL) {
|
||||
// unsigned char unique_data[UNIQUEID_BYTES];
|
||||
// RAND_bytes(unique_data, UNIQUEID_BYTES);
|
||||
// for (int i = 0; i < UNIQUEID_BYTES; i++) {
|
||||
// sprintf(unique_id + (i * 2), "%02x", unique_data[i]);
|
||||
// }
|
||||
// fd = fopen(uniqueFilePath, "w");
|
||||
// if (fd == NULL)
|
||||
// return GS_FAILED;
|
||||
//
|
||||
// fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
// } else {
|
||||
// fread(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
// }
|
||||
// fclose(fd);
|
||||
// unique_id[UNIQUEID_CHARS] = 0;
|
||||
//
|
||||
// return GS_OK;
|
||||
//}
|
||||
//
|
||||
//static int load_cert(const char* keyDirectory) {
|
||||
// char certificateFilePath[PATH_MAX];
|
||||
// snprintf(certificateFilePath, PATH_MAX, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
//
|
||||
// char keyFilePath[PATH_MAX];
|
||||
// snprintf(&keyFilePath[0], PATH_MAX, "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
//
|
||||
// FILE *fd = fopen(certificateFilePath, "r");
|
||||
// if (fd == NULL) {
|
||||
// printf("Generating certificate...");
|
||||
// CERT_KEY_PAIR cert = mkcert_generate();
|
||||
// printf("done\n");
|
||||
//
|
||||
// char p12FilePath[PATH_MAX];
|
||||
// snprintf(p12FilePath, PATH_MAX, "%s/%s", keyDirectory, P12_FILE_NAME);
|
||||
//
|
||||
// mkcert_save(certificateFilePath, p12FilePath, keyFilePath, cert);
|
||||
// mkcert_free(cert);
|
||||
// fd = fopen(certificateFilePath, "r");
|
||||
// }
|
||||
//
|
||||
// if (fd == NULL) {
|
||||
// gs_error = "Can't open certificate file";
|
||||
// return GS_FAILED;
|
||||
// }
|
||||
//
|
||||
// if (!(cert = PEM_read_X509(fd, NULL, NULL, NULL))) {
|
||||
// gs_error = "Error loading cert into memory";
|
||||
// return GS_FAILED;
|
||||
// }
|
||||
//
|
||||
// rewind(fd);
|
||||
//
|
||||
// int c;
|
||||
// int length = 0;
|
||||
// while ((c = fgetc(fd)) != EOF) {
|
||||
// sprintf(cert_hex + length, "%02x", c);
|
||||
// length += 2;
|
||||
// }
|
||||
// cert_hex[length] = 0;
|
||||
//
|
||||
// fclose(fd);
|
||||
//
|
||||
// fd = fopen(keyFilePath, "r");
|
||||
// if (fd == NULL) {
|
||||
// gs_error = "Error loading key into memory";
|
||||
// return GS_FAILED;
|
||||
// }
|
||||
//
|
||||
// PEM_read_PrivateKey(fd, &privateKey, NULL, NULL);
|
||||
// fclose(fd);
|
||||
//
|
||||
// return GS_OK;
|
||||
//}
|
||||
//
|
||||
//static int load_server_status(PSERVER_DATA server) {
|
||||
//
|
||||
// uuid_t uuid;
|
||||
// char uuid_str[37];
|
||||
//
|
||||
// int ret;
|
||||
// char url[4096];
|
||||
// int i;
|
||||
//
|
||||
// i = 0;
|
||||
// do {
|
||||
// char *pairedText = NULL;
|
||||
// char *currentGameText = NULL;
|
||||
// char *stateText = NULL;
|
||||
// char *serverCodecModeSupportText = NULL;
|
||||
//
|
||||
// ret = GS_INVALID;
|
||||
//
|
||||
// uuid_generate_random(uuid);
|
||||
// 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.
|
||||
//
|
||||
// printf("server->serverInfo.address: %s\n", server->serverInfo.address);
|
||||
// printf("unique_id: %s\n", unique_id);
|
||||
// printf("uuid_str: %s\n", uuid_str);
|
||||
//
|
||||
// snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
|
||||
// i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? 47984 : 47989, unique_id, uuid_str);
|
||||
//
|
||||
// PHTTP_DATA data = http_create_data();
|
||||
// if (data == NULL) {
|
||||
// ret = GS_OUT_OF_MEMORY;
|
||||
// goto cleanup;
|
||||
// }
|
||||
// if (http_request(url, data) != GS_OK) {
|
||||
// ret = GS_IO_ERROR;
|
||||
// goto cleanup;
|
||||
// }
|
||||
//
|
||||
// if (xml_status(data->memory, data->size) == GS_ERROR) {
|
||||
// ret = GS_ERROR;
|
||||
// goto cleanup;
|
||||
// }
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) {
|
||||
// goto cleanup;
|
||||
// }
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "hostname", &server->hostname) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// // These fields are present on all version of GFE that this client supports
|
||||
// if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
// goto cleanup;
|
||||
//
|
||||
// server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
// server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
// server->supports4K = serverCodecModeSupportText != NULL;
|
||||
// server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
//
|
||||
// if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// // 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:
|
||||
// if (data != NULL)
|
||||
// http_free_data(data);
|
||||
//
|
||||
// if (pairedText != NULL)
|
||||
// free(pairedText);
|
||||
//
|
||||
// if (currentGameText != NULL)
|
||||
// free(currentGameText);
|
||||
//
|
||||
// if (serverCodecModeSupportText != NULL)
|
||||
// free(serverCodecModeSupportText);
|
||||
//
|
||||
// i++;
|
||||
// } while (ret != GS_OK && i < 2);
|
||||
//
|
||||
// if (ret == GS_OK && !server->unsupported) {
|
||||
// if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
// gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again";
|
||||
// ret = GS_UNSUPPORTED_VERSION;
|
||||
// } else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) {
|
||||
// gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again.";
|
||||
// ret = GS_UNSUPPORTED_VERSION;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
@ -300,46 +329,50 @@ static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
|
|||
}
|
||||
|
||||
static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *slen, EVP_PKEY *pkey) {
|
||||
LOG_FMT("mlen = %i\n", mlen);
|
||||
int result = GS_FAILED;
|
||||
|
||||
*sig = NULL;
|
||||
*slen = 0;
|
||||
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_create(); DEBUG_EMPTY_LOG
|
||||
if (ctx == NULL)
|
||||
return GS_FAILED;
|
||||
|
||||
const EVP_MD *md = EVP_get_digestbyname("SHA256");
|
||||
const EVP_MD *md = EVP_get_digestbyname("SHA256");DEBUG_EMPTY_LOG
|
||||
if (md == NULL)
|
||||
goto cleanup;
|
||||
|
||||
int rc = EVP_DigestInit_ex(ctx, md, NULL);
|
||||
int rc = EVP_DigestInit_ex(ctx, md, NULL);DEBUG_EMPTY_LOG
|
||||
if (rc != 1)
|
||||
goto cleanup;
|
||||
|
||||
rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
|
||||
rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);DEBUG_EMPTY_LOG
|
||||
if (rc != 1)
|
||||
goto cleanup;
|
||||
|
||||
rc = EVP_DigestSignUpdate(ctx, msg, mlen);
|
||||
rc = EVP_DigestSignUpdate(ctx, msg, mlen);DEBUG_EMPTY_LOG
|
||||
if (rc != 1)
|
||||
goto cleanup;
|
||||
|
||||
size_t req = 0;
|
||||
rc = EVP_DigestSignFinal(ctx, NULL, &req);
|
||||
rc = EVP_DigestSignFinal(ctx, NULL, &req);DEBUG_EMPTY_LOG
|
||||
if (rc != 1 || !(req > 0))
|
||||
goto cleanup;
|
||||
|
||||
*sig = OPENSSL_malloc(req);
|
||||
*sig = OPENSSL_malloc(req);DEBUG_EMPTY_LOG
|
||||
if (*sig == NULL)
|
||||
goto cleanup;
|
||||
|
||||
*slen = req;
|
||||
rc = EVP_DigestSignFinal(ctx, *sig, slen);
|
||||
rc = EVP_DigestSignFinal(ctx, *sig, slen);DEBUG_EMPTY_LOG
|
||||
//LOG_FMT("msg = %s\n", msg);
|
||||
LOG_FMT("rc = %i\n", rc);
|
||||
if (rc != 1 || req != *slen)
|
||||
goto cleanup;
|
||||
|
||||
result = GS_OK;
|
||||
DEBUG_EMPTY_LOG
|
||||
|
||||
cleanup:
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
|
@ -499,8 +532,8 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
char challenge_response_data_enc[48];
|
||||
char challenge_response_data[48];
|
||||
char challenge_response_data_enc[64];
|
||||
char challenge_response_data[64];
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]);
|
||||
}
|
||||
|
@ -791,19 +824,19 @@ int gs_quit_app(PSERVER_DATA server) {
|
|||
http_free_data(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
mkdirtree(keyDirectory);
|
||||
if (load_unique_id(keyDirectory) != GS_OK)
|
||||
return GS_FAILED;
|
||||
|
||||
if (load_cert(keyDirectory))
|
||||
return GS_FAILED;
|
||||
|
||||
http_init(keyDirectory, log_level);
|
||||
|
||||
LiInitializeServerInformation(&server->serverInfo);
|
||||
server->serverInfo.address = address;
|
||||
server->unsupported = unsupported;
|
||||
return load_server_status(server);
|
||||
}
|
||||
//
|
||||
//int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
// mkdirtree(keyDirectory);
|
||||
// if (load_unique_id(keyDirectory) != GS_OK)
|
||||
// return GS_FAILED;
|
||||
//
|
||||
// if (load_cert(keyDirectory))
|
||||
// return GS_FAILED;
|
||||
//
|
||||
// http_init(keyDirectory, log_level);
|
||||
//
|
||||
// LiInitializeServerInformation(&server->serverInfo);
|
||||
// server->serverInfo.address = address;
|
||||
// server->unsupported = unsupported;
|
||||
// return load_server_status(server);
|
||||
//}
|
||||
|
|
|
@ -43,7 +43,7 @@ typedef struct _SERVER_DATA {
|
|||
} SERVER_DATA, *PSERVER_DATA;
|
||||
|
||||
int mkdirtree(const char* directory);
|
||||
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
|
||||
//int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
|
||||
int gs_app_boxart(PSERVER_DATA server, int app_id, char **art_data, size_t *art_data_size);
|
||||
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);
|
||||
|
|
|
@ -29,7 +29,7 @@ typedef struct _HTTP_DATA {
|
|||
size_t size;
|
||||
} HTTP_DATA, *PHTTP_DATA;
|
||||
|
||||
int http_init(const char* keyDirectory, int logLevel);
|
||||
//int http_init(const char* keyDirectory, int logLevel);
|
||||
PHTTP_DATA http_create_data();
|
||||
int http_request(char* url, PHTTP_DATA data);
|
||||
void http_free_data(PHTTP_DATA data);
|
||||
|
|
|
@ -33,7 +33,6 @@ static const int SERIAL = 0;
|
|||
static const int NUM_YEARS = 10;
|
||||
|
||||
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years);
|
||||
int add_ext(X509 *cert, int nid, char *value);
|
||||
|
||||
CERT_KEY_PAIR mkcert_generate() {
|
||||
BIO *bio_err;
|
||||
|
@ -44,16 +43,12 @@ CERT_KEY_PAIR mkcert_generate() {
|
|||
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
|
||||
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS);
|
||||
|
||||
p12 = PKCS12_create("limelight", "GameStream", pkey, x509, NULL, 0, 0, 0, 0, 0);
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
ENGINE_cleanup();
|
||||
#endif
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
if (p12 == NULL) {
|
||||
printf("Error generating a valid PKCS12 certificate.\n");
|
||||
}
|
||||
|
||||
BIO_free(bio_err);
|
||||
|
||||
|
@ -82,105 +77,49 @@ void mkcert_save(const char* certFile, const char* p12File, const char* keyPairF
|
|||
}
|
||||
|
||||
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
|
||||
X509 *x;
|
||||
EVP_PKEY *pk;
|
||||
RSA *rsa;
|
||||
X509_NAME *name = NULL;
|
||||
|
||||
if (*pkeyp == NULL) {
|
||||
if ((pk=EVP_PKEY_new()) == NULL) {
|
||||
abort();
|
||||
return(0);
|
||||
}
|
||||
} else {
|
||||
pk = *pkeyp;
|
||||
}
|
||||
|
||||
if (*x509p == NULL) {
|
||||
if ((x = X509_new()) == NULL) {
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
x = *x509p;
|
||||
}
|
||||
|
||||
if ((rsa = RSA_new()) == NULL)
|
||||
goto err;
|
||||
|
||||
X509* cert = X509_new();
|
||||
EVP_PKEY* pk = EVP_PKEY_new();
|
||||
BIGNUM* bne = BN_new();
|
||||
if (bne == NULL) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
RSA* rsa = RSA_new();
|
||||
|
||||
BN_set_word(bne, RSA_F4);
|
||||
if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
RSA_generate_key_ex(rsa, bits, bne, NULL);
|
||||
|
||||
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
EVP_PKEY_assign_RSA(pk, rsa);
|
||||
|
||||
X509_set_version(x, 2);
|
||||
ASN1_INTEGER_set(X509_get_serialNumber(x), serial);
|
||||
X509_gmtime_adj(X509_get_notBefore(x), 0);
|
||||
X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*365*years);
|
||||
X509_set_pubkey(x, pk);
|
||||
X509_set_version(cert, 2);
|
||||
ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
X509_gmtime_adj(X509_get_notBefore(cert), 0);
|
||||
X509_gmtime_adj(X509_get_notAfter(cert), 60 * 60 * 24 * 365 * years);
|
||||
#else
|
||||
ASN1_TIME* before = ASN1_STRING_dup(X509_get0_notBefore(cert));
|
||||
ASN1_TIME* after = ASN1_STRING_dup(X509_get0_notAfter(cert));
|
||||
|
||||
name = X509_get_subject_name(x);
|
||||
X509_gmtime_adj(before, 0);
|
||||
X509_gmtime_adj(after, 60 * 60 * 24 * 365 * years);
|
||||
|
||||
/* This function creates and adds the entry, working out the
|
||||
* correct string type and performing checks on its length.
|
||||
*/
|
||||
X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char*)"NVIDIA GameStream Client", -1, -1, 0);
|
||||
X509_set1_notBefore(cert, before);
|
||||
X509_set1_notAfter(cert, after);
|
||||
|
||||
/* Its self signed so set the issuer name to be the same as the
|
||||
* subject.
|
||||
*/
|
||||
X509_set_issuer_name(x, name);
|
||||
ASN1_STRING_free(before);
|
||||
ASN1_STRING_free(after);
|
||||
#endif
|
||||
|
||||
/* Add various extensions: standard extensions */
|
||||
add_ext(x, NID_key_usage, "critical,digitalSignature,keyEncipherment");
|
||||
X509_set_pubkey(cert, pk);
|
||||
|
||||
add_ext(x, NID_subject_key_identifier, "hash");
|
||||
X509_NAME* name = X509_get_subject_name(cert);
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
|
||||
(const unsigned char*)"NVIDIA GameStream Client",
|
||||
-1, -1, 0);
|
||||
X509_set_issuer_name(cert, name);
|
||||
|
||||
if (!X509_sign(x, pk, EVP_sha256())) {
|
||||
goto err;
|
||||
}
|
||||
X509_sign(cert, pk, EVP_sha256());
|
||||
|
||||
*x509p = x;
|
||||
BN_free(bne);
|
||||
|
||||
*x509p = cert;
|
||||
*pkeyp = pk;
|
||||
|
||||
return(1);
|
||||
err:
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Add extension using V3 code: we can set the config file as NULL
|
||||
* because we wont reference any other sections.
|
||||
*/
|
||||
|
||||
int add_ext(X509 *cert, int nid, char *value)
|
||||
{
|
||||
X509_EXTENSION *ex;
|
||||
X509V3_CTX ctx;
|
||||
/* This sets the 'context' of the extensions. */
|
||||
/* No configuration database */
|
||||
X509V3_set_ctx_nodb(&ctx);
|
||||
/* Issuer and subject certs: both the target since it is self signed,
|
||||
* no request and no CRL
|
||||
*/
|
||||
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
|
||||
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
|
||||
if (!ex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
X509_add_ext(cert, ex, -1);
|
||||
X509_EXTENSION_free(ex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,11 +85,21 @@
|
|||
3652F08A245C8569001FABF3 /* ContentWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3652F089245C8569001FABF3 /* ContentWindow.cpp */; };
|
||||
3661D2F92469D1940060EE24 /* FFmpegVideoDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3661D2F72469D1940060EE24 /* FFmpegVideoDecoder.cpp */; };
|
||||
3661D2FF2469E0C00060EE24 /* GLVideoRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3661D2FD2469E0C00060EE24 /* GLVideoRenderer.cpp */; };
|
||||
3661D3022469EFFA0060EE24 /* RetroAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3661D3002469EFFA0060EE24 /* RetroAudioRenderer.cpp */; };
|
||||
367CD95A245DE25F00A95738 /* StreamWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 367CD958245DE25F00A95738 /* StreamWindow.cpp */; };
|
||||
36A0C0372461DBA30083289C /* AddHostButton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A0C0352461DBA30083289C /* AddHostButton.cpp */; };
|
||||
36A0C03A2461E4C00083289C /* SettingsWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A0C0382461E4C00083289C /* SettingsWindow.cpp */; };
|
||||
36A0C03D2461F03C0083289C /* Settings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A0C03B2461F03C0083289C /* Settings.cpp */; };
|
||||
36ADAB6A246F171D0058D1D6 /* RetroAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3661D3002469EFFA0060EE24 /* RetroAudioRenderer.cpp */; };
|
||||
36ADABB0246F262D0058D1D6 /* compare.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB90246F262D0058D1D6 /* compare.c */; };
|
||||
36ADABB1246F262D0058D1D6 /* unpack.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB97246F262D0058D1D6 /* unpack.c */; };
|
||||
36ADABB2246F262D0058D1D6 /* isnull.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB99246F262D0058D1D6 /* isnull.c */; };
|
||||
36ADABB3246F262D0058D1D6 /* copy.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB9B246F262D0058D1D6 /* copy.c */; };
|
||||
36ADABB4246F262D0058D1D6 /* unparse.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB9F246F262D0058D1D6 /* unparse.c */; };
|
||||
36ADABB5246F262D0058D1D6 /* pack.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA2246F262D0058D1D6 /* pack.c */; };
|
||||
36ADABB6246F262D0058D1D6 /* clear.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA5246F262D0058D1D6 /* clear.c */; };
|
||||
36ADABB7246F262D0058D1D6 /* randutils.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA6246F262D0058D1D6 /* randutils.c */; };
|
||||
36ADABB9246F262D0058D1D6 /* gen_uuid.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA9246F262D0058D1D6 /* gen_uuid.c */; };
|
||||
36ADABBA246F262D0058D1D6 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABAA246F262D0058D1D6 /* parse.c */; };
|
||||
36B406982459F5CB005BD903 /* moonlight_glfw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36B406962459F460005BD903 /* moonlight_glfw.cpp */; };
|
||||
36D3F8442469B5C400CDEF9B /* MoonlightSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36D3F8422469B5C400CDEF9B /* MoonlightSession.cpp */; };
|
||||
36DBDE9A2450BCD50057C8D3 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DBDE992450BCD50057C8D3 /* CoreFoundation.framework */; };
|
||||
|
@ -100,6 +110,10 @@
|
|||
36DFE0CE2459FAB100FC51CE /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DBDEA22450C2640057C8D3 /* OpenGL.framework */; };
|
||||
36DFE217245A278700FC51CE /* glsym_gl.c in Sources */ = {isa = PBXBuildFile; fileRef = 36DFE0DB245A1FEC00FC51CE /* glsym_gl.c */; };
|
||||
36DFE218245A278900FC51CE /* rglgen.c in Sources */ = {isa = PBXBuildFile; fileRef = 36DFE0DE245A1FEC00FC51CE /* rglgen.c */; };
|
||||
36E6378C246FFFF30032F5FB /* CryptoManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E6378A246FFFF30032F5FB /* CryptoManager.cpp */; };
|
||||
36E63790247010C70032F5FB /* Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E6378E247010C70032F5FB /* Data.cpp */; };
|
||||
36E63794247030290032F5FB /* NetworkClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E63792247030290032F5FB /* NetworkClient.cpp */; };
|
||||
36E63797247036A30032F5FB /* ServerInfoRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E63795247036A30032F5FB /* ServerInfoRequest.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -285,6 +299,8 @@
|
|||
3661D30B246B1C680060EE24 /* build_ffmpeg_lakka_switch.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_ffmpeg_lakka_switch.sh; sourceTree = "<group>"; };
|
||||
367CD958245DE25F00A95738 /* StreamWindow.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StreamWindow.cpp; sourceTree = "<group>"; };
|
||||
367CD959245DE25F00A95738 /* StreamWindow.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StreamWindow.hpp; sourceTree = "<group>"; };
|
||||
369445A82466CE2700786D0A /* Makefile.libnx */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; path = Makefile.libnx; sourceTree = "<group>"; };
|
||||
369445A92466E2B000786D0A /* moonlight_libnx.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = moonlight_libnx.c; sourceTree = "<group>"; };
|
||||
36A0C0352461DBA30083289C /* AddHostButton.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AddHostButton.cpp; sourceTree = "<group>"; };
|
||||
36A0C0362461DBA30083289C /* AddHostButton.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AddHostButton.hpp; sourceTree = "<group>"; };
|
||||
36A0C0382461E4C00083289C /* SettingsWindow.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SettingsWindow.cpp; sourceTree = "<group>"; };
|
||||
|
@ -292,6 +308,22 @@
|
|||
36A0C03B2461F03C0083289C /* Settings.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Settings.cpp; sourceTree = "<group>"; };
|
||||
36A0C03C2461F03C0083289C /* Settings.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Settings.hpp; sourceTree = "<group>"; };
|
||||
36A0C03E2461FFF10083289C /* build_opus_lakka_switch.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_opus_lakka_switch.sh; sourceTree = "<group>"; };
|
||||
36ADAB90246F262D0058D1D6 /* compare.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compare.c; sourceTree = "<group>"; };
|
||||
36ADAB94246F262D0058D1D6 /* uuid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uuid.h; sourceTree = "<group>"; };
|
||||
36ADAB96246F262D0058D1D6 /* randutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = randutils.h; sourceTree = "<group>"; };
|
||||
36ADAB97246F262D0058D1D6 /* unpack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unpack.c; sourceTree = "<group>"; };
|
||||
36ADAB98246F262D0058D1D6 /* uuidd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uuidd.h; sourceTree = "<group>"; };
|
||||
36ADAB99246F262D0058D1D6 /* isnull.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = isnull.c; sourceTree = "<group>"; };
|
||||
36ADAB9B246F262D0058D1D6 /* copy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = copy.c; sourceTree = "<group>"; };
|
||||
36ADAB9F246F262D0058D1D6 /* unparse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unparse.c; sourceTree = "<group>"; };
|
||||
36ADABA1246F262D0058D1D6 /* c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c.h; sourceTree = "<group>"; };
|
||||
36ADABA2246F262D0058D1D6 /* pack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pack.c; sourceTree = "<group>"; };
|
||||
36ADABA5246F262D0058D1D6 /* clear.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = clear.c; sourceTree = "<group>"; };
|
||||
36ADABA6246F262D0058D1D6 /* randutils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = randutils.c; sourceTree = "<group>"; };
|
||||
36ADABA8246F262D0058D1D6 /* uuid_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = uuid_time.c; sourceTree = "<group>"; };
|
||||
36ADABA9246F262D0058D1D6 /* gen_uuid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gen_uuid.c; sourceTree = "<group>"; };
|
||||
36ADABAA246F262D0058D1D6 /* parse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parse.c; sourceTree = "<group>"; };
|
||||
36ADABAF246F262D0058D1D6 /* uuidP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uuidP.h; sourceTree = "<group>"; };
|
||||
36B406962459F460005BD903 /* moonlight_glfw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moonlight_glfw.cpp; sourceTree = "<group>"; };
|
||||
36D3F8422469B5C400CDEF9B /* MoonlightSession.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MoonlightSession.cpp; sourceTree = "<group>"; };
|
||||
36D3F8432469B5C400CDEF9B /* MoonlightSession.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MoonlightSession.hpp; sourceTree = "<group>"; };
|
||||
|
@ -316,6 +348,14 @@
|
|||
36DFE0DF245A1FEC00FC51CE /* rglgen_headers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = rglgen_headers.h; sourceTree = "<group>"; };
|
||||
36DFE0E1245A1FEC00FC51CE /* glsym_es3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = glsym_es3.h; sourceTree = "<group>"; };
|
||||
36DFE0E3245A1FEC00FC51CE /* glsym_gl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = glsym_gl.h; sourceTree = "<group>"; };
|
||||
36E6378A246FFFF30032F5FB /* CryptoManager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoManager.cpp; sourceTree = "<group>"; };
|
||||
36E6378B246FFFF30032F5FB /* CryptoManager.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CryptoManager.hpp; sourceTree = "<group>"; };
|
||||
36E6378E247010C70032F5FB /* Data.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Data.cpp; sourceTree = "<group>"; };
|
||||
36E6378F247010C70032F5FB /* Data.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Data.hpp; sourceTree = "<group>"; };
|
||||
36E63792247030290032F5FB /* NetworkClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkClient.cpp; sourceTree = "<group>"; };
|
||||
36E63793247030290032F5FB /* NetworkClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NetworkClient.hpp; sourceTree = "<group>"; };
|
||||
36E63795247036A30032F5FB /* ServerInfoRequest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ServerInfoRequest.cpp; sourceTree = "<group>"; };
|
||||
36E63796247036A30032F5FB /* ServerInfoRequest.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ServerInfoRequest.hpp; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -623,9 +663,34 @@
|
|||
path = video;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
36ADAB8F246F262D0058D1D6 /* libuuid */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36ADAB90246F262D0058D1D6 /* compare.c */,
|
||||
36ADAB94246F262D0058D1D6 /* uuid.h */,
|
||||
36ADAB96246F262D0058D1D6 /* randutils.h */,
|
||||
36ADAB97246F262D0058D1D6 /* unpack.c */,
|
||||
36ADAB98246F262D0058D1D6 /* uuidd.h */,
|
||||
36ADAB99246F262D0058D1D6 /* isnull.c */,
|
||||
36ADAB9B246F262D0058D1D6 /* copy.c */,
|
||||
36ADAB9F246F262D0058D1D6 /* unparse.c */,
|
||||
36ADABA1246F262D0058D1D6 /* c.h */,
|
||||
36ADABA2246F262D0058D1D6 /* pack.c */,
|
||||
36ADABA5246F262D0058D1D6 /* clear.c */,
|
||||
36ADABA6246F262D0058D1D6 /* randutils.c */,
|
||||
36ADABA8246F262D0058D1D6 /* uuid_time.c */,
|
||||
36ADABA9246F262D0058D1D6 /* gen_uuid.c */,
|
||||
36ADABAA246F262D0058D1D6 /* parse.c */,
|
||||
36ADABAF246F262D0058D1D6 /* uuidP.h */,
|
||||
);
|
||||
path = libuuid;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
36B406932459F41E005BD903 /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36E6379124702FEC0032F5FB /* network */,
|
||||
36E63789246FFFDC0032F5FB /* crypto */,
|
||||
36DFE0D5245A1FEC00FC51CE /* glsym */,
|
||||
36DFE0CA2459FA3F00FC51CE /* nanogui_resources */,
|
||||
36DFDCF12459F79000FC51CE /* ui */,
|
||||
|
@ -640,6 +705,7 @@
|
|||
3652F084245C6CFC001FABF3 /* libretro.h */,
|
||||
3652F085245C6CFC001FABF3 /* moonlight_libretro.cpp */,
|
||||
36B406962459F460005BD903 /* moonlight_glfw.cpp */,
|
||||
369445A92466E2B000786D0A /* moonlight_libnx.c */,
|
||||
);
|
||||
path = src;
|
||||
sourceTree = "<group>";
|
||||
|
@ -680,6 +746,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
3602C3C0245DC7E300368900 /* Makefile */,
|
||||
369445A82466CE2700786D0A /* Makefile.libnx */,
|
||||
36A0C03E2461FFF10083289C /* build_opus_lakka_switch.sh */,
|
||||
3661D30B246B1C680060EE24 /* build_ffmpeg_lakka_switch.sh */,
|
||||
3652F006245C2918001FABF3 /* libgamestream */,
|
||||
|
@ -727,6 +794,7 @@
|
|||
36DFDCF62459F80600FC51CE /* third_party */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36ADAB8F246F262D0058D1D6 /* libuuid */,
|
||||
3652F014245C292B001FABF3 /* moonlight-common-c */,
|
||||
3652ECC3245B3AFF001FABF3 /* nanogui */,
|
||||
);
|
||||
|
@ -757,6 +825,28 @@
|
|||
path = glsym;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
36E63789246FFFDC0032F5FB /* crypto */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36E6378E247010C70032F5FB /* Data.cpp */,
|
||||
36E6378F247010C70032F5FB /* Data.hpp */,
|
||||
36E6378A246FFFF30032F5FB /* CryptoManager.cpp */,
|
||||
36E6378B246FFFF30032F5FB /* CryptoManager.hpp */,
|
||||
);
|
||||
path = crypto;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
36E6379124702FEC0032F5FB /* network */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36E63792247030290032F5FB /* NetworkClient.cpp */,
|
||||
36E63793247030290032F5FB /* NetworkClient.hpp */,
|
||||
36E63795247036A30032F5FB /* ServerInfoRequest.cpp */,
|
||||
36E63796247036A30032F5FB /* ServerInfoRequest.hpp */,
|
||||
);
|
||||
path = network;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -835,11 +925,12 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3661D3022469EFFA0060EE24 /* RetroAudioRenderer.cpp in Sources */,
|
||||
36E63794247030290032F5FB /* NetworkClient.cpp in Sources */,
|
||||
36DFE218245A278900FC51CE /* rglgen.c in Sources */,
|
||||
3652F075245C292B001FABF3 /* VideoStream.c in Sources */,
|
||||
3652EFE0245B3B00001FABF3 /* imageview.cpp in Sources */,
|
||||
3652EFDB245B3B00001FABF3 /* texture.cpp in Sources */,
|
||||
36ADABB6246F262D0058D1D6 /* clear.c in Sources */,
|
||||
36A0C03D2461F03C0083289C /* Settings.cpp in Sources */,
|
||||
3652F079245C292B001FABF3 /* RtpReorderQueue.c in Sources */,
|
||||
3652F065245C292B001FABF3 /* list.c in Sources */,
|
||||
|
@ -853,8 +944,11 @@
|
|||
3652EFDE245B3B00001FABF3 /* tabwidget.cpp in Sources */,
|
||||
3652F07A245C292B001FABF3 /* SdpGenerator.c in Sources */,
|
||||
3652EFEB245B3B00001FABF3 /* imagepanel.cpp in Sources */,
|
||||
36ADABB7246F262D0058D1D6 /* randutils.c in Sources */,
|
||||
36E63790247010C70032F5FB /* Data.cpp in Sources */,
|
||||
36A0C0372461DBA30083289C /* AddHostButton.cpp in Sources */,
|
||||
3652F06E245C292B001FABF3 /* rs.c in Sources */,
|
||||
36ADABB2246F262D0058D1D6 /* isnull.c in Sources */,
|
||||
3652EFD1245B3B00001FABF3 /* colorpicker.cpp in Sources */,
|
||||
3652F010245C2919001FABF3 /* xml.c in Sources */,
|
||||
3652F066245C292B001FABF3 /* compress.c in Sources */,
|
||||
|
@ -877,7 +971,9 @@
|
|||
3652EFE1245B3B00001FABF3 /* progressbar.cpp in Sources */,
|
||||
3652EFF5245B3B00001FABF3 /* popupbutton.cpp in Sources */,
|
||||
3652EFF0245B3B00001FABF3 /* colorwheel.cpp in Sources */,
|
||||
36ADABBA246F262D0058D1D6 /* parse.c in Sources */,
|
||||
3652F068245C292B001FABF3 /* unix.c in Sources */,
|
||||
36E6378C246FFFF30032F5FB /* CryptoManager.cpp in Sources */,
|
||||
3652F07B245C292B001FABF3 /* RtspParser.c in Sources */,
|
||||
3652EFCF245B3B00001FABF3 /* checkbox.cpp in Sources */,
|
||||
36A0C03A2461E4C00083289C /* SettingsWindow.cpp in Sources */,
|
||||
|
@ -896,25 +992,33 @@
|
|||
3652EFD6245B3B00001FABF3 /* window.cpp in Sources */,
|
||||
3652F071245C292B001FABF3 /* InputStream.c in Sources */,
|
||||
3652EFDD245B3B00001FABF3 /* texture_gl.cpp in Sources */,
|
||||
36ADAB6A246F171D0058D1D6 /* RetroAudioRenderer.cpp in Sources */,
|
||||
3652F013245C2919001FABF3 /* http.c in Sources */,
|
||||
3652F069245C292B001FABF3 /* peer.c in Sources */,
|
||||
3602C3B7245D903000368900 /* HostButton.cpp in Sources */,
|
||||
3652EFD0245B3B00001FABF3 /* vscrollpanel.cpp in Sources */,
|
||||
36ADABB9246F262D0058D1D6 /* gen_uuid.c in Sources */,
|
||||
3652F06D245C292B001FABF3 /* win32.c in Sources */,
|
||||
36E63797247036A30032F5FB /* ServerInfoRequest.cpp in Sources */,
|
||||
3652F012245C2919001FABF3 /* mkcert.c in Sources */,
|
||||
3652F002245B6961001FABF3 /* AddHostWindow.cpp in Sources */,
|
||||
3652EFF8245B4EE2001FABF3 /* Application.cpp in Sources */,
|
||||
36ADABB0246F262D0058D1D6 /* compare.c in Sources */,
|
||||
3652F07E245C292B001FABF3 /* AudioStream.c in Sources */,
|
||||
3652F076245C292B001FABF3 /* Connection.c in Sources */,
|
||||
3652F011245C2919001FABF3 /* client.c in Sources */,
|
||||
36ADABB3246F262D0058D1D6 /* copy.c in Sources */,
|
||||
3652EFE4245B3B00001FABF3 /* combobox.cpp in Sources */,
|
||||
3603E93C246316400051287D /* InputController.cpp in Sources */,
|
||||
36ADABB1246F262D0058D1D6 /* unpack.c in Sources */,
|
||||
36ADABB5246F262D0058D1D6 /* pack.c in Sources */,
|
||||
3652EFF2245B3B00001FABF3 /* textbox.cpp in Sources */,
|
||||
3652F074245C292B001FABF3 /* Platform.c in Sources */,
|
||||
3661D2FF2469E0C00060EE24 /* GLVideoRenderer.cpp in Sources */,
|
||||
3652EFCD245B3B00001FABF3 /* widget.cpp in Sources */,
|
||||
3652F073245C292B001FABF3 /* PlatformSockets.c in Sources */,
|
||||
3652F08A245C8569001FABF3 /* ContentWindow.cpp in Sources */,
|
||||
36ADABB4246F262D0058D1D6 /* unparse.c in Sources */,
|
||||
3652EFE5245B3B00001FABF3 /* theme.cpp in Sources */,
|
||||
367CD95A245DE25F00A95738 /* StreamWindow.cpp in Sources */,
|
||||
3652F07F245C292B001FABF3 /* LinkedBlockingQueue.c in Sources */,
|
||||
|
@ -981,7 +1085,7 @@
|
|||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
/usr/local/include,
|
||||
"/usr/local/opt/openssl@1.1/include",
|
||||
"\"/usr/local/Cellar/openssl@1.0/1.0.2t/include\"",
|
||||
"\"$(SRCROOT)/third_party/nanogui/include\"",
|
||||
"\"$(SRCROOT)/third_party/nanogui/ext/nanovg/src\"",
|
||||
"\"$(SRCROOT)/src/nanogui_resources\"",
|
||||
|
@ -991,7 +1095,7 @@
|
|||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
/usr/local/lib,
|
||||
"/usr/local/opt/openssl@1.1/lib",
|
||||
"\"/usr/local/Cellar/openssl@1.0/1.0.2t/lib\"",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
|
@ -1071,7 +1175,7 @@
|
|||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
/usr/local/include,
|
||||
"/usr/local/opt/openssl@1.1/include",
|
||||
"\"/usr/local/Cellar/openssl@1.0/1.0.2t/include\"",
|
||||
"\"$(SRCROOT)/third_party/nanogui/include\"",
|
||||
"\"$(SRCROOT)/third_party/nanogui/ext/nanovg/src\"",
|
||||
"\"$(SRCROOT)/src/nanogui_resources\"",
|
||||
|
@ -1081,7 +1185,7 @@
|
|||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
/usr/local/lib,
|
||||
"/usr/local/opt/openssl@1.1/lib",
|
||||
"\"/usr/local/Cellar/openssl@1.0/1.0.2t/lib\"",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
|
|
4
run.sh
Executable file
4
run.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
make -f Makefile.libnx
|
||||
/opt/devkitpro/tools/bin/nxlink -s moonlight.nro
|
|
@ -1,7 +1,7 @@
|
|||
#include "GameStreamClient.hpp"
|
||||
#include "ServerInfoRequest.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
@ -9,12 +9,42 @@
|
|||
#include <fstream>
|
||||
#include <future>
|
||||
#include <nanogui/nanogui.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
static std::mutex m_async_mutex;
|
||||
static std::vector<std::function<void()>> m_tasks;
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
static void task_loop() {
|
||||
Thread thread;
|
||||
threadCreate(
|
||||
&thread,
|
||||
[](void* a) {
|
||||
while (1) {
|
||||
std::vector<std::function<void()>> m_tasks_copy; {
|
||||
std::lock_guard<std::mutex> guard(m_async_mutex);
|
||||
m_tasks_copy = m_tasks;
|
||||
m_tasks.clear();
|
||||
}
|
||||
|
||||
for (auto task: m_tasks_copy) {
|
||||
task();
|
||||
}
|
||||
|
||||
usleep(500'000);
|
||||
}
|
||||
},
|
||||
NULL,
|
||||
NULL,
|
||||
0x10000,
|
||||
0x2C,
|
||||
-2
|
||||
);
|
||||
threadStart(&thread);
|
||||
|
||||
}
|
||||
#else
|
||||
static void task_loop() {
|
||||
auto thread = std::thread([](){
|
||||
while (1) {
|
||||
|
@ -33,6 +63,7 @@ static void task_loop() {
|
|||
});
|
||||
thread.detach();
|
||||
}
|
||||
#endif
|
||||
|
||||
void perform_async(std::function<void()> task) {
|
||||
std::lock_guard<std::mutex> guard(m_async_mutex);
|
||||
|
@ -48,15 +79,14 @@ void GameStreamClient::connect(const std::string &address, ServerCallback<SERVER
|
|||
|
||||
perform_async([this, address, callback] {
|
||||
// TODO: mem leak here :(
|
||||
std::string key_dir = Settings::settings()->working_dir() + "/key";
|
||||
int status = gs_init(&m_server_data[address], (char *)(new std::string(address))->c_str(), key_dir.c_str(), 0, false);
|
||||
int status = ServerInfoRequest::request(address, &m_server_data[address]);
|
||||
|
||||
nanogui::async([this, address, callback, status] {
|
||||
if (status == GS_OK) {
|
||||
if (status == NetworkClientOK) {
|
||||
Settings::settings()->add_host(address);
|
||||
callback(Result<SERVER_DATA>::success(m_server_data[address]));
|
||||
callback(GSResult<SERVER_DATA>::success(m_server_data[address]));
|
||||
} else {
|
||||
callback(Result<SERVER_DATA>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
callback(GSResult<SERVER_DATA>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -64,7 +94,7 @@ void GameStreamClient::connect(const std::string &address, ServerCallback<SERVER
|
|||
|
||||
void GameStreamClient::pair(const std::string &address, const std::string &pin, ServerCallback<bool> callback) {
|
||||
if (m_server_data.count(address) == 0) {
|
||||
callback(Result<bool>::failure("Firstly call connect()..."));
|
||||
callback(GSResult<bool>::failure("Firstly call connect()..."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -73,9 +103,9 @@ void GameStreamClient::pair(const std::string &address, const std::string &pin,
|
|||
|
||||
nanogui::async([callback, status] {
|
||||
if (status == GS_OK) {
|
||||
callback(Result<bool>::success(true));
|
||||
callback(GSResult<bool>::success(true));
|
||||
} else {
|
||||
callback(Result<bool>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
callback(GSResult<bool>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -83,7 +113,7 @@ void GameStreamClient::pair(const std::string &address, const std::string &pin,
|
|||
|
||||
void GameStreamClient::applist(const std::string &address, ServerCallback<PAPP_LIST> callback) {
|
||||
if (m_server_data.count(address) == 0) {
|
||||
callback(Result<PAPP_LIST>::failure("Firstly call connect() & pair()..."));
|
||||
callback(GSResult<PAPP_LIST>::failure("Firstly call connect() & pair()..."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -94,9 +124,9 @@ void GameStreamClient::applist(const std::string &address, ServerCallback<PAPP_L
|
|||
|
||||
nanogui::async([this, address, callback, status] {
|
||||
if (status == GS_OK) {
|
||||
callback(Result<PAPP_LIST>::success(m_app_list[address]));
|
||||
callback(GSResult<PAPP_LIST>::success(m_app_list[address]));
|
||||
} else {
|
||||
callback(Result<PAPP_LIST>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
callback(GSResult<PAPP_LIST>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -104,7 +134,7 @@ void GameStreamClient::applist(const std::string &address, ServerCallback<PAPP_L
|
|||
|
||||
void GameStreamClient::app_boxart(const std::string &address, int app_id, ServerCallback<std::pair<char*, size_t>> callback) {
|
||||
if (m_server_data.count(address) == 0) {
|
||||
callback(Result<std::pair<char*, size_t>>::failure("Firstly call connect() & pair()..."));
|
||||
callback(GSResult<std::pair<char*, size_t>>::failure("Firstly call connect() & pair()..."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -116,9 +146,9 @@ void GameStreamClient::app_boxart(const std::string &address, int app_id, Server
|
|||
|
||||
nanogui::async([this, callback, data, size, status] {
|
||||
if (status == GS_OK) {
|
||||
callback(Result<std::pair<char*, size_t>>::success(std::make_pair(data, size)));
|
||||
callback(GSResult<std::pair<char*, size_t>>::success(std::make_pair(data, size)));
|
||||
} else {
|
||||
callback(Result<std::pair<char*, size_t>>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
callback(GSResult<std::pair<char*, size_t>>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -126,7 +156,7 @@ void GameStreamClient::app_boxart(const std::string &address, int app_id, Server
|
|||
|
||||
void GameStreamClient::start(const std::string &address, STREAM_CONFIGURATION config, int app_id, ServerCallback<STREAM_CONFIGURATION> callback) {
|
||||
if (m_server_data.count(address) == 0) {
|
||||
callback(Result<STREAM_CONFIGURATION>::failure("Firstly call connect() & pair()..."));
|
||||
callback(GSResult<STREAM_CONFIGURATION>::failure("Firstly call connect() & pair()..."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -137,9 +167,9 @@ void GameStreamClient::start(const std::string &address, STREAM_CONFIGURATION co
|
|||
|
||||
nanogui::async([this, callback, status] {
|
||||
if (status == GS_OK) {
|
||||
callback(Result<STREAM_CONFIGURATION>::success(m_config));
|
||||
callback(GSResult<STREAM_CONFIGURATION>::success(m_config));
|
||||
} else {
|
||||
callback(Result<STREAM_CONFIGURATION>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
callback(GSResult<STREAM_CONFIGURATION>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -147,7 +177,7 @@ void GameStreamClient::start(const std::string &address, STREAM_CONFIGURATION co
|
|||
|
||||
void GameStreamClient::quit(const std::string &address, ServerCallback<bool> callback) {
|
||||
if (m_server_data.count(address) == 0) {
|
||||
callback(Result<bool>::failure("Firstly call connect() & pair()..."));
|
||||
callback(GSResult<bool>::failure("Firstly call connect() & pair()..."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -156,9 +186,9 @@ void GameStreamClient::quit(const std::string &address, ServerCallback<bool> cal
|
|||
|
||||
nanogui::async([this, callback, status] {
|
||||
if (status == GS_OK) {
|
||||
callback(Result<bool>::success(true));
|
||||
callback(GSResult<bool>::success(true));
|
||||
} else {
|
||||
callback(Result<bool>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
callback(GSResult<bool>::failure(gs_error != NULL ? gs_error : "Unknown error..."));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,13 +13,13 @@ extern "C" {
|
|||
extern void perform_async(std::function<void()> task);
|
||||
|
||||
template <typename T>
|
||||
struct Result {
|
||||
struct GSResult {
|
||||
public:
|
||||
static Result success(T value) {
|
||||
static GSResult success(T value) {
|
||||
return result(value, "", true);
|
||||
}
|
||||
|
||||
static Result failure(std::string error) {
|
||||
static GSResult failure(std::string error) {
|
||||
return result(T(), error, false);
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
static Result result(T value, std::string error, bool isSuccess) {
|
||||
Result result;
|
||||
static GSResult result(T value, std::string error, bool isSuccess) {
|
||||
GSResult result;
|
||||
result._value = value;
|
||||
result._error = error;
|
||||
result._isSuccess = isSuccess;
|
||||
|
@ -49,7 +49,7 @@ private:
|
|||
bool _isSuccess = false;
|
||||
};
|
||||
|
||||
template<class T> using ServerCallback = const std::function<void(Result<T>)>;
|
||||
template<class T> using ServerCallback = const std::function<void(GSResult<T>)>;
|
||||
|
||||
class GameStreamClient {
|
||||
public:
|
||||
|
|
274
src/crypto/CryptoManager.cpp
Normal file
274
src/crypto/CryptoManager.cpp
Normal file
|
@ -0,0 +1,274 @@
|
|||
#include "CryptoManager.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include <string.h>
|
||||
#include <cstdlib>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
static const int SHA1_HASH_LENGTH = 20;
|
||||
static const int SHA256_HASH_LENGTH = 32;
|
||||
|
||||
static Data* m_cert = nullptr;
|
||||
static Data* m_p12 = nullptr;
|
||||
static Data* m_key = nullptr;
|
||||
|
||||
bool CryptoManager::certs_exists() {
|
||||
if (load_certs()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CryptoManager::load_certs() {
|
||||
if (m_key == nullptr || m_cert == nullptr || m_p12 == nullptr) {
|
||||
auto key_dir = Settings::settings()->working_dir() + "/key/";
|
||||
mkdirtree(key_dir.c_str());
|
||||
|
||||
Data cert = Data::read_from_file(key_dir + CERTIFICATE_FILE_NAME);
|
||||
Data p12 = Data::read_from_file(key_dir + P12_FILE_NAME);
|
||||
Data key = Data::read_from_file(key_dir + KEY_FILE_NAME);
|
||||
|
||||
if (!cert.is_empty() && !p12.is_empty() && !key.is_empty()) {
|
||||
m_cert = &cert;
|
||||
m_p12 = &p12;
|
||||
m_key = &key;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoManager::generate_certs() {
|
||||
auto cert_key_pair = mkcert_generate();
|
||||
|
||||
Data cert = get_cert_from_cert_key_pair(&cert_key_pair);
|
||||
Data p12 = get_p12_from_cert_key_pair(&cert_key_pair);
|
||||
Data key = get_key_from_cert_key_pair(&cert_key_pair);
|
||||
|
||||
auto key_dir = Settings::settings()->working_dir() + "/key/";
|
||||
mkdirtree(key_dir.c_str());
|
||||
|
||||
if (!cert.is_empty() && !p12.is_empty() && !key.is_empty()) {
|
||||
cert.write_to_file(key_dir + CERTIFICATE_FILE_NAME);
|
||||
p12.write_to_file(key_dir + P12_FILE_NAME);
|
||||
key.write_to_file(key_dir + KEY_FILE_NAME);
|
||||
m_cert = &cert;
|
||||
m_p12 = &p12;
|
||||
m_key = &key;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Data CryptoManager::SHA1_hash_data(Data data) {
|
||||
char sha1[SHA1_HASH_LENGTH];
|
||||
SHA1((unsigned char*)data.bytes(), data.size(), (unsigned char*)sha1);
|
||||
return Data(sha1, sizeof(sha1));
|
||||
}
|
||||
|
||||
Data CryptoManager::SHA256_hash_data(Data data) {
|
||||
char sha256[SHA256_HASH_LENGTH];
|
||||
SHA256((unsigned char*)data.bytes(), data.size(), (unsigned char*)sha256);
|
||||
return Data(sha256, sizeof(sha256));
|
||||
}
|
||||
|
||||
Data CryptoManager::create_AES_key_from_salt_SHA1(Data salted_pin) {
|
||||
return SHA1_hash_data(salted_pin).subdata(16);
|
||||
}
|
||||
|
||||
Data CryptoManager::create_AES_key_from_salt_SHA256(Data salted_pin) {
|
||||
return SHA256_hash_data(salted_pin).subdata(16);
|
||||
}
|
||||
|
||||
static int get_encrypt_size(Data data) {
|
||||
// the size is the length of the data ceiling to the nearest 16 bytes
|
||||
return (((int)data.size() + 15) / 16) * 16;
|
||||
}
|
||||
|
||||
Data CryptoManager::aes_encrypt(Data data, Data key) {
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key((unsigned char*)key.bytes(), 128, &aes_key);
|
||||
int size = get_encrypt_size(data);
|
||||
unsigned char* buffer = (unsigned char*)malloc(size);
|
||||
unsigned char* block_rounded_buffer = (unsigned char*)calloc(1, size);
|
||||
memcpy(block_rounded_buffer, data.bytes(), data.size());
|
||||
|
||||
// AES_encrypt only encrypts the first 16 bytes so iterate the entire buffer
|
||||
int block_offset = 0;
|
||||
while (block_offset < size) {
|
||||
AES_encrypt(block_rounded_buffer + block_offset, buffer + block_offset, &aes_key);
|
||||
block_offset += 16;
|
||||
}
|
||||
|
||||
Data encrypted_data = Data((char*)buffer, size);
|
||||
free(buffer);
|
||||
free(block_rounded_buffer);
|
||||
return encrypted_data;
|
||||
}
|
||||
|
||||
Data CryptoManager::aes_decrypt(Data data, Data key) {
|
||||
AES_KEY aes_key;
|
||||
AES_set_decrypt_key((unsigned char*)key.bytes(), 128, &aes_key);
|
||||
unsigned char* buffer = (unsigned char*)malloc(data.size());
|
||||
|
||||
// AES_decrypt only decrypts the first 16 bytes so iterate the entire buffer
|
||||
int block_offset = 0;
|
||||
while (block_offset < data.size()) {
|
||||
AES_decrypt((unsigned char*)data.bytes() + block_offset, buffer + block_offset, &aes_key);
|
||||
block_offset += 16;
|
||||
}
|
||||
|
||||
Data decrypted_data = Data(buffer, data.size());
|
||||
free(buffer);
|
||||
return decrypted_data;
|
||||
}
|
||||
|
||||
Data CryptoManager::pem_to_der(Data pem_cert_bytes) {
|
||||
X509* x509;
|
||||
|
||||
BIO* bio = BIO_new_mem_buf((unsigned char*)pem_cert_bytes.bytes(), pem_cert_bytes.size());
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
i2d_X509_bio(bio, x509);
|
||||
|
||||
BUF_MEM* mem;
|
||||
BIO_get_mem_ptr(bio, &mem);
|
||||
|
||||
Data ret = Data(mem->data, mem->length);
|
||||
BIO_free(bio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CryptoManager::verify_signature(Data data, Data signature, Data cert) {
|
||||
X509* x509;
|
||||
BIO* bio = BIO_new_mem_buf(cert.bytes(), cert.size());
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
if (!x509) {
|
||||
LOG("Unable to parse certificate in memory...\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
EVP_PKEY* pub_key = X509_get_pubkey(x509);
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
mdctx = EVP_MD_CTX_create();
|
||||
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pub_key);
|
||||
EVP_DigestVerifyUpdate(mdctx, data.bytes(), data.size());
|
||||
int result = EVP_DigestVerifyFinal(mdctx, (unsigned char*)signature.bytes(), signature.size());
|
||||
|
||||
X509_free(x509);
|
||||
EVP_PKEY_free(pub_key);
|
||||
EVP_MD_CTX_destroy(mdctx);
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
Data CryptoManager::sign_data(Data data, Data key) {
|
||||
BIO* bio = BIO_new_mem_buf(key.bytes(), key.size());
|
||||
|
||||
EVP_PKEY* pkey;
|
||||
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
if (!pkey) {
|
||||
LOG("Unable to parse private key in memory!");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
mdctx = EVP_MD_CTX_create();
|
||||
EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey);
|
||||
EVP_DigestSignUpdate(mdctx, data.bytes(), data.size());
|
||||
size_t slen;
|
||||
EVP_DigestSignFinal(mdctx, NULL, &slen);
|
||||
unsigned char* signature = (unsigned char*)malloc(slen);
|
||||
int result = EVP_DigestSignFinal(mdctx, signature, &slen);
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
EVP_MD_CTX_destroy(mdctx);
|
||||
|
||||
if (result <= 0) {
|
||||
free(signature);
|
||||
LOG("Unable to sign data!");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
Data signed_data = Data(signature, slen);
|
||||
free(signature);
|
||||
return signed_data;
|
||||
}
|
||||
|
||||
Data CryptoManager::get_signature_from_cert(Data cert) {
|
||||
BIO* bio = BIO_new_mem_buf(cert.bytes(), cert.size());
|
||||
X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
|
||||
if (!x509) {
|
||||
LOG("Unable to parse certificate in memory!\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10002000L)
|
||||
ASN1_BIT_STRING *asn_signature = x509->signature;
|
||||
#elif (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
ASN1_BIT_STRING *asn_signature;
|
||||
X509_get0_signature(&asn_signature, NULL, x509);
|
||||
#else
|
||||
const ASN1_BIT_STRING *asn_signature;
|
||||
X509_get0_signature(&asn_signature, NULL, x509);
|
||||
#endif
|
||||
|
||||
Data sig = Data(asn_signature->data, asn_signature->length);
|
||||
X509_free(x509);
|
||||
return sig;
|
||||
}
|
||||
|
||||
Data CryptoManager::get_key_from_cert_key_pair(PCERT_KEY_PAIR cert_key_pair) {
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
PEM_write_bio_PrivateKey(bio, cert_key_pair->pkey, NULL, NULL, 0, NULL, NULL);
|
||||
#else
|
||||
PEM_write_bio_PrivateKey_traditional(bio, cert_key_pair->pkey, NULL, NULL, 0, NULL, NULL);
|
||||
#endif
|
||||
|
||||
BUF_MEM* mem;
|
||||
BIO_get_mem_ptr(bio, &mem);
|
||||
Data data = Data(mem->data, mem->length);
|
||||
BIO_free(bio);
|
||||
return data;
|
||||
}
|
||||
|
||||
Data CryptoManager::get_p12_from_cert_key_pair(PCERT_KEY_PAIR cert_key_pair) {
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
|
||||
i2d_PKCS12_bio(bio, cert_key_pair->p12);
|
||||
|
||||
BUF_MEM* mem;
|
||||
BIO_get_mem_ptr(bio, &mem);
|
||||
Data data = Data(mem->data, mem->length);
|
||||
BIO_free(bio);
|
||||
return data;
|
||||
}
|
||||
|
||||
Data CryptoManager::get_cert_from_cert_key_pair(PCERT_KEY_PAIR cert_key_pair) {
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
|
||||
PEM_write_bio_X509(bio, cert_key_pair->x509);
|
||||
|
||||
BUF_MEM* mem;
|
||||
BIO_get_mem_ptr(bio, &mem);
|
||||
Data data = Data(mem->data, mem->length);
|
||||
BIO_free(bio);
|
||||
return data;
|
||||
}
|
33
src/crypto/CryptoManager.hpp
Normal file
33
src/crypto/CryptoManager.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <stdio.h>
|
||||
#include "Data.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "mkcert.h"
|
||||
#include "client.h"
|
||||
}
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CERTIFICATE_FILE_NAME "client.pem"
|
||||
#define P12_FILE_NAME "client.p12"
|
||||
#define KEY_FILE_NAME "key.pem"
|
||||
|
||||
class CryptoManager {
|
||||
public:
|
||||
static bool certs_exists();
|
||||
static bool load_certs();
|
||||
static bool generate_certs();
|
||||
static Data SHA1_hash_data(Data data);
|
||||
static Data SHA256_hash_data(Data data);
|
||||
static Data create_AES_key_from_salt_SHA1(Data salted_pin);
|
||||
static Data create_AES_key_from_salt_SHA256(Data salted_pin);
|
||||
static Data aes_encrypt(Data data, Data key);
|
||||
static Data aes_decrypt(Data data, Data key);
|
||||
static Data pem_to_der(Data pem_cert_bytes);
|
||||
static bool verify_signature(Data data, Data signature, Data cert);
|
||||
static Data sign_data(Data data, Data key);
|
||||
static Data get_signature_from_cert(Data cert);
|
||||
static Data get_key_from_cert_key_pair(PCERT_KEY_PAIR cert_key_pair);
|
||||
static Data get_p12_from_cert_key_pair(PCERT_KEY_PAIR cert_key_pair);
|
||||
static Data get_cert_from_cert_key_pair(PCERT_KEY_PAIR cert_key_pair);
|
||||
};
|
106
src/crypto/Data.cpp
Normal file
106
src/crypto/Data.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "Data.hpp"
|
||||
#include <string.h>
|
||||
#include <cstdlib>
|
||||
#include "Log.h"
|
||||
|
||||
Data::Data(char* bytes, size_t size) {
|
||||
if (bytes && size > 0) {
|
||||
m_bytes = (char *)malloc(sizeof(char) * size);
|
||||
memcpy(m_bytes, bytes, sizeof(char) * size);
|
||||
m_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
Data::Data(size_t capacity) {
|
||||
if (capacity > 0) {
|
||||
m_bytes = (char *)malloc(sizeof(char) * capacity);
|
||||
m_size = capacity;
|
||||
}
|
||||
}
|
||||
|
||||
Data::~Data() {
|
||||
if (m_bytes) {
|
||||
free(m_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
Data Data::subdata(size_t size) {
|
||||
if (size > m_size) {
|
||||
LOG("Invalid data length...\n");
|
||||
exit(-1);
|
||||
}
|
||||
return Data(m_bytes, size);
|
||||
}
|
||||
|
||||
Data::Data(const Data& that): Data(that.m_bytes, that.m_size) {}
|
||||
|
||||
Data& Data::operator=(const Data& that) {
|
||||
if (this != &that) {
|
||||
if (m_bytes) {
|
||||
free(m_bytes);
|
||||
}
|
||||
|
||||
m_bytes = (char *)malloc(sizeof(char) * that.m_size);
|
||||
memcpy(m_bytes, that.m_bytes, sizeof(char) * that.m_size);
|
||||
m_size = that.m_size;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Data Data::random_bytes(size_t size) {
|
||||
char* bytes = (char*)malloc(sizeof(char) * size);
|
||||
arc4random_buf(bytes, size);
|
||||
Data random_data(bytes, size);
|
||||
free(bytes);
|
||||
return random_data;
|
||||
}
|
||||
|
||||
Data Data::read_from_file(std::string path) {
|
||||
FILE* f = fopen(path.c_str(), "r");
|
||||
if (f) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
int size = (int)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
char* buffer = (char*)malloc(size);
|
||||
fread(buffer, 1, size, f);
|
||||
Data data(buffer, size);
|
||||
free(buffer);
|
||||
fclose(f);
|
||||
return data;
|
||||
}
|
||||
return Data();
|
||||
}
|
||||
|
||||
Data Data::hex_to_bytes() const {
|
||||
Data data(m_size / 2);
|
||||
char byte_chars[3] = {'\0','\0','\0'};
|
||||
unsigned long whole_byte;
|
||||
|
||||
int i = 0, counter = 0;
|
||||
while (i < m_size) {
|
||||
byte_chars[0] = m_bytes[i++];
|
||||
byte_chars[1] = m_bytes[i++];
|
||||
whole_byte = strtoul(byte_chars, NULL, 16);
|
||||
data.m_bytes[counter++] = whole_byte;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
Data Data::bytes_to_hex() const {
|
||||
int counter = 0;
|
||||
Data hex(m_size * 2);
|
||||
char fmt[3] = {'\0','\0','\0'};
|
||||
for (int i = 0; i < m_size; i++) {
|
||||
sprintf(fmt, "%02X", m_bytes[i]);
|
||||
hex.m_bytes[counter++] = fmt[0];
|
||||
hex.m_bytes[counter++] = fmt[1];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
void Data::write_to_file(std::string path) {
|
||||
FILE *f = fopen(path.c_str(), "w");
|
||||
fwrite(m_bytes, m_size, 1, f);
|
||||
fclose(f);
|
||||
}
|
42
src/crypto/Data.hpp
Normal file
42
src/crypto/Data.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <stdio.h>
|
||||
#include <string>
|
||||
#pragma once
|
||||
|
||||
class Data {
|
||||
public:
|
||||
Data(): Data(0) {};
|
||||
Data(char* bytes, size_t size);
|
||||
Data(unsigned char* bytes, size_t size): Data((char *)bytes, size) {};
|
||||
Data(size_t capacity);
|
||||
|
||||
~Data();
|
||||
|
||||
char* bytes() const {
|
||||
return m_bytes;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
Data subdata(size_t size);
|
||||
|
||||
Data(const Data& that);
|
||||
Data& operator=(const Data& that);
|
||||
|
||||
static Data random_bytes(size_t size);
|
||||
static Data read_from_file(std::string path);
|
||||
|
||||
Data hex_to_bytes() const;
|
||||
Data bytes_to_hex() const;
|
||||
|
||||
bool is_empty() const {
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
void write_to_file(std::string path);
|
||||
|
||||
private:
|
||||
char* m_bytes = nullptr;
|
||||
size_t m_size = 0;
|
||||
};
|
|
@ -1,12 +1,25 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "glsym/glsym.h"
|
||||
#include "Application.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include "Limelight.h"
|
||||
#include "libretro.h"
|
||||
#include "InputController.hpp"
|
||||
#include <curl/curl.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <glad/glad.h>
|
||||
#include <switch.h>
|
||||
#else
|
||||
#include "glsym/glsym.h"
|
||||
#endif
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "Data.hpp"
|
||||
#include "NetworkClient.hpp"
|
||||
#include "ServerInfoRequest.hpp"
|
||||
|
||||
extern retro_input_state_t input_state_cb;
|
||||
|
||||
|
@ -49,6 +62,9 @@ static int16_t glfw_input_state_cb(unsigned port, unsigned device, unsigned inde
|
|||
int main(int argc, const char * argv[]) {
|
||||
input_state_cb = glfw_input_state_cb;
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
glfwInit();
|
||||
|
||||
glfwSetErrorCallback([](int i, const char *error) {
|
||||
|
@ -62,9 +78,14 @@ int main(int argc, const char * argv[]) {
|
|||
|
||||
GLFWwindow* window = glfwCreateWindow(1280, 720, "Test", NULL, NULL);
|
||||
glfwMakeContextCurrent(window);
|
||||
rglgen_resolve_symbols(glfwGetProcAddress);
|
||||
glfwSwapInterval(1);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
||||
#else
|
||||
rglgen_resolve_symbols(glfwGetProcAddress);
|
||||
#endif
|
||||
|
||||
glfwSetCursorPosCallback(window, [](GLFWwindow *w, double x, double y) {
|
||||
mouse_x = x;
|
||||
mouse_y = y;
|
||||
|
@ -90,7 +111,15 @@ int main(int argc, const char * argv[]) {
|
|||
glfwGetWindowSize(window, &width, &height);
|
||||
glfwGetFramebufferSize(window, &fb_width, &fb_height);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
Settings::settings()->set_working_dir("sdmc:/switch/moonlight");
|
||||
#else
|
||||
Settings::settings()->set_working_dir("/Users/rock88/Documents/RetroArch/system/moonlight");
|
||||
#endif
|
||||
|
||||
// SERVER_DATA d5;
|
||||
// std::string a = "www.google.ru";
|
||||
// ServerInfoRequest::request(a, &d5);
|
||||
|
||||
nanogui::init();
|
||||
nanogui::ref<Application> app = new Application(Size(width, height), Size(fb_width, fb_height));
|
||||
|
@ -98,6 +127,15 @@ int main(int argc, const char * argv[]) {
|
|||
nanogui::setup(1.0 / 15.0);
|
||||
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
#ifdef __SWITCH__
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if (kDown & KEY_PLUS) {
|
||||
printf("Exit...\n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
InputController::controller()->handle_input(width, height);
|
||||
|
@ -110,5 +148,6 @@ int main(int argc, const char * argv[]) {
|
|||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
|
40
src/moonlight_libnx.c
Normal file
40
src/moonlight_libnx.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <netinet/in.h>
|
||||
#include <stdint.h>
|
||||
#include <switch.h>
|
||||
#include "libretro.h"
|
||||
|
||||
retro_audio_sample_batch_t audio_batch_cb = NULL;
|
||||
retro_input_state_t input_state_cb = NULL;
|
||||
|
||||
uint32_t htonl(uint32_t hostlong) {
|
||||
return __builtin_bswap32(hostlong);
|
||||
}
|
||||
|
||||
uint16_t htons(uint16_t hostshort) {
|
||||
return __builtin_bswap16(hostshort);
|
||||
}
|
||||
|
||||
uint32_t ntohl(uint32_t netlong) {
|
||||
return __builtin_bswap32(netlong);
|
||||
}
|
||||
|
||||
uint16_t ntohs(uint16_t netshort) {
|
||||
return __builtin_bswap16(netshort);
|
||||
}
|
||||
|
||||
int sigaction(int a, const struct sigaction* b, struct sigaction* c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nxlink_sock = -1;
|
||||
|
||||
void userAppInit() {
|
||||
socketInitializeDefault();
|
||||
nxlink_sock = nxlinkStdio();
|
||||
}
|
||||
|
||||
void userAppExit() {
|
||||
if (nxlink_sock != -1)
|
||||
close(nxlink_sock);
|
||||
socketExit();
|
||||
}
|
96
src/network/NetworkClient.cpp
Normal file
96
src/network/NetworkClient.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include "NetworkClient.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include "CryptoManager.hpp"
|
||||
#include "errors.h"
|
||||
#include "Log.h"
|
||||
#include <curl/curl.h>
|
||||
|
||||
static CURL *curl;
|
||||
static bool init_curl_completed = false;
|
||||
|
||||
struct HTTP_DATA {
|
||||
char *memory;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
size_t realsize = size * nmemb;
|
||||
HTTP_DATA* mem = (HTTP_DATA *)userp;
|
||||
|
||||
mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
|
||||
if (mem->memory == NULL)
|
||||
return 0;
|
||||
|
||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
static void init_curl() {
|
||||
if (init_curl_completed) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key_directory = Settings::settings()->working_dir() + "/key";
|
||||
|
||||
curl = curl_easy_init();
|
||||
|
||||
char certificateFilePath[4096];
|
||||
sprintf(certificateFilePath, "%s/%s", key_directory.c_str(), CERTIFICATE_FILE_NAME);
|
||||
|
||||
char keyFilePath[4096];
|
||||
sprintf(&keyFilePath[0], "%s/%s", key_directory.c_str(), KEY_FILE_NAME);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,"PEM");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERT, certificateFilePath);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLKEY, keyFilePath);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write_curl);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L);
|
||||
|
||||
init_curl_completed = true;
|
||||
}
|
||||
|
||||
int NetworkClient::perform(std::string url, bool use_https, Data* out) {
|
||||
init_curl();
|
||||
|
||||
std::string _url;
|
||||
|
||||
if (use_https) {
|
||||
_url = "https://" + url;
|
||||
} else {
|
||||
_url = "http://" + url;
|
||||
}
|
||||
|
||||
LOG_FMT("Perform %s\n", _url.c_str());
|
||||
|
||||
HTTP_DATA* http_data = (HTTP_DATA*)malloc(sizeof(HTTP_DATA));
|
||||
http_data->memory = (char*)malloc(1);
|
||||
http_data->size = 0;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, http_data);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, _url.c_str());
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
gs_error = curl_easy_strerror(res);
|
||||
return NetworkClientError;
|
||||
} else if (http_data->memory == NULL) {
|
||||
gs_error = "Out of memory";
|
||||
return NetworkClientError;
|
||||
}
|
||||
|
||||
*out = Data(http_data->memory, (int)http_data->size);
|
||||
|
||||
free(http_data->memory);
|
||||
free(http_data);
|
||||
|
||||
return NetworkClientOK;
|
||||
}
|
13
src/network/NetworkClient.hpp
Normal file
13
src/network/NetworkClient.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "Data.hpp"
|
||||
#include <string>
|
||||
#pragma once
|
||||
|
||||
#define NetworkClientOK 0
|
||||
#define NetworkClientError 1
|
||||
|
||||
static const std::string unique_id = "0123456789ABCDEF";
|
||||
|
||||
class NetworkClient {
|
||||
public:
|
||||
static int perform(std::string url, bool use_https, Data* out);
|
||||
};
|
109
src/network/ServerInfoRequest.cpp
Normal file
109
src/network/ServerInfoRequest.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
#include "ServerInfoRequest.hpp"
|
||||
#include "CryptoManager.hpp"
|
||||
#include "Log.h"
|
||||
|
||||
extern "C" {
|
||||
#include "errors.h"
|
||||
}
|
||||
|
||||
int ServerInfoRequest::request(std::string address, PSERVER_DATA server) {
|
||||
LiInitializeServerInformation(&server->serverInfo);
|
||||
char* addr = (char *)malloc(sizeof(char) * address.length() + 1);
|
||||
addr[address.length()] = '\0';
|
||||
memcpy(addr, address.c_str(), sizeof(char) * address.length());
|
||||
server->serverInfo.address = addr;
|
||||
|
||||
int result = NetworkClientError;
|
||||
char url[1024];
|
||||
|
||||
bool certs_exists = CryptoManager::certs_exists();
|
||||
|
||||
if (certs_exists) {
|
||||
snprintf(url, sizeof(url), "%s:47984/serverinfo?uniqueid=%s", server->serverInfo.address, unique_id.c_str());
|
||||
} else {
|
||||
snprintf(url, sizeof(url), "%s:47989/serverinfo", server->serverInfo.address);
|
||||
}
|
||||
|
||||
Data data;
|
||||
if (NetworkClient::perform(url, certs_exists, &data) != NetworkClientOK) {
|
||||
return NetworkClientError;
|
||||
}
|
||||
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
|
||||
if (xml_status(data.bytes(), data.size()) == GS_ERROR)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "currentgame", ¤tGameText) != GS_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "PairStatus", &pairedText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "state", &stateText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "gputype", &server->gpuType) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "GsVersion", &server->gsVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "hostname", &server->hostname) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_modelist(data.bytes(), data.size(), &server->modes) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// These fields are present on all version of GFE that this client supports
|
||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
goto cleanup;
|
||||
|
||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
server->supports4K = serverCodecModeSupportText != NULL;
|
||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
|
||||
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// 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;
|
||||
}
|
||||
result = NetworkClientOK;
|
||||
|
||||
cleanup:
|
||||
if (pairedText != NULL)
|
||||
free(pairedText);
|
||||
|
||||
if (currentGameText != NULL)
|
||||
free(currentGameText);
|
||||
|
||||
if (serverCodecModeSupportText != NULL)
|
||||
free(serverCodecModeSupportText);
|
||||
|
||||
if (result == GS_OK && !server->unsupported) {
|
||||
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again";
|
||||
result = NetworkClientError;
|
||||
} else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again.";
|
||||
result = NetworkClientError;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
12
src/network/ServerInfoRequest.hpp
Normal file
12
src/network/ServerInfoRequest.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "NetworkClient.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "client.h"
|
||||
}
|
||||
|
||||
#pragma once
|
||||
|
||||
class ServerInfoRequest {
|
||||
public:
|
||||
static int request(std::string address, PSERVER_DATA server);
|
||||
};
|
|
@ -1,5 +1,9 @@
|
|||
#include "IVideoRenderer.hpp"
|
||||
#ifdef __SWITCH__
|
||||
#include <glad/glad.h>
|
||||
#else
|
||||
#include "glsym.h"
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
class GLVideoRenderer: public IVideoRenderer {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "HostButton.hpp"
|
||||
#include "GameStreamClient.hpp"
|
||||
#include "nanovg.h"
|
||||
#include "Log.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace nanogui;
|
||||
|
@ -22,6 +23,7 @@ HostButton::HostButton(Widget* parent, const std::string &address): Button(paren
|
|||
m_is_paired = result.value().paired;
|
||||
m_host_status_icon = m_is_paired ? FA_CHECK : FA_QUESTION;
|
||||
} else {
|
||||
LOG_FMT("Error: %s\n", result.error().c_str());
|
||||
m_is_paired = false;
|
||||
m_is_active = false;
|
||||
m_host_status_icon = FA_POWER_OFF;
|
||||
|
|
2
third_party/nanogui
vendored
2
third_party/nanogui
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 56eb83fd44745accc765171afb46a7c60abc3067
|
||||
Subproject commit 48528d30a2c2036b7e006915984cae6f44f60f34
|
Loading…
Reference in a new issue