Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ set(SUNSHINE_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/nvhttp/sessions.h"
"${CMAKE_SOURCE_DIR}/src/nvhttp/url_utils.cpp"
"${CMAKE_SOURCE_DIR}/src/nvhttp/url_utils.h"
"${CMAKE_SOURCE_DIR}/src/remote_connect/api.cpp"
"${CMAKE_SOURCE_DIR}/src/remote_connect/api.h"
"${CMAKE_SOURCE_DIR}/src/remote_connect/easytier.cpp"
"${CMAKE_SOURCE_DIR}/src/remote_connect/easytier.h"
"${CMAKE_SOURCE_DIR}/src/remote_connect/invite.cpp"
"${CMAKE_SOURCE_DIR}/src/remote_connect/invite.h"
"${CMAKE_SOURCE_DIR}/src/remote_connect/pairing.cpp"
"${CMAKE_SOURCE_DIR}/src/remote_connect/pairing.h"
"${CMAKE_SOURCE_DIR}/src/remote_connect/service.cpp"
"${CMAKE_SOURCE_DIR}/src/remote_connect/service.h"
"${CMAKE_SOURCE_DIR}/src/remote_connect/types.h"
"${CMAKE_SOURCE_DIR}/src/nvhttp_stream_start.cpp"
"${CMAKE_SOURCE_DIR}/src/nvhttp_stream_start.h"
"${CMAKE_SOURCE_DIR}/src/abr.cpp"
Expand Down
130 changes: 130 additions & 0 deletions cmake/packaging/FetchEasyTier.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# FetchEasyTier.cmake — download the pinned EasyTier host runtime.
#
# The archive is fetched from the official EasyTier release, verified before
# extraction, and installed as a private Sunshine runtime under tools/easytier.

include_guard(GLOBAL)

if(NOT WIN32)
return()
endif()

option(FETCH_EASYTIER "Download the EasyTier runtime used by Remote Connect" ON)
option(EASYTIER_REQUIRED "Treat a missing EasyTier runtime as a configuration error" ON)
set(EASYTIER_VERSION "v2.6.4")
set(EASYTIER_CACHE_DIR "${CMAKE_BINARY_DIR}/_easytier" CACHE PATH "EasyTier runtime cache")

if(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]64$" OR
CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$")
set(_EASYTIER_ARCH "arm64")
set(_EASYTIER_SHA256 "37023f8a3451c9234b17ee2089a03dc344ce90d803b5b359cb6c46682b0549b4")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(_EASYTIER_ARCH "i686")
set(_EASYTIER_SHA256 "bf557daeccc5525d95b8a230c339d75554fb52d82d1e050e9a5202c92c02e09e")
else()
set(_EASYTIER_ARCH "x86_64")
set(_EASYTIER_SHA256 "27af91e270e554709b048bd32327fefd2dfce5062ae1e8701af7550c6f525f84")
endif()

set(_EASYTIER_BASENAME "easytier-windows-${_EASYTIER_ARCH}-${EASYTIER_VERSION}")
set(_EASYTIER_ARCHIVE "${EASYTIER_CACHE_DIR}/${_EASYTIER_BASENAME}.zip")
set(EASYTIER_RUNTIME_DIR "${EASYTIER_CACHE_DIR}/easytier-windows-${_EASYTIER_ARCH}"
CACHE PATH "Extracted EasyTier runtime directory" FORCE)
set(EASYTIER_LICENSE "${EASYTIER_CACHE_DIR}/LICENSE-EasyTier.txt"
CACHE FILEPATH "EasyTier license file" FORCE)

function(_easytier_download url output expected_sha256)
if(EXISTS "${output}")
file(SHA256 "${output}" _cached_sha256)
if(_cached_sha256 STREQUAL expected_sha256)
return()
endif()
message(WARNING "Cached EasyTier artifact failed verification; downloading it again")
file(REMOVE "${output}")
endif()

get_filename_component(_output_dir "${output}" DIRECTORY)
file(MAKE_DIRECTORY "${_output_dir}")
message(STATUS "Downloading pinned EasyTier artifact: ${url}")
find_program(_EASYTIER_CURL curl)
if(_EASYTIER_CURL)
execute_process(
COMMAND "${_EASYTIER_CURL}" -fsSL --retry 3 -o "${output}" "${url}"
RESULT_VARIABLE _code
ERROR_VARIABLE _message)
else()
file(DOWNLOAD "${url}" "${output}" STATUS _status TLS_VERIFY ON)
list(GET _status 0 _code)
list(GET _status 1 _message)
endif()
if(NOT _code EQUAL 0)
file(REMOVE "${output}")
message(WARNING "EasyTier download failed (${_code}): ${_message}")
return()
endif()

file(SHA256 "${output}" _actual_sha256)
if(NOT _actual_sha256 STREQUAL expected_sha256)
file(REMOVE "${output}")
message(WARNING
"EasyTier artifact SHA-256 mismatch\n"
"expected: ${expected_sha256}\n"
"actual: ${_actual_sha256}")
endif()
endfunction()

set(_EASYTIER_REQUIRED_FILES
easytier-core.exe
Packet.dll
WinDivert64.sys
wintun.dll)

if(FETCH_EASYTIER)
_easytier_download(
"https://github.com/EasyTier/EasyTier/releases/download/${EASYTIER_VERSION}/${_EASYTIER_BASENAME}.zip"
"${_EASYTIER_ARCHIVE}"
"${_EASYTIER_SHA256}")
_easytier_download(
"https://raw.githubusercontent.com/EasyTier/EasyTier/${EASYTIER_VERSION}/LICENSE"
"${EASYTIER_LICENSE}"
"e3a994d82e644b03a792a930f574002658412f62407f5fee083f2555c5f23118")

if(EXISTS "${_EASYTIER_ARCHIVE}")
# Always derive the packaged runtime from the verified archive. Otherwise a
# tampered stale extraction could have its digest compiled into Sunshine.
file(REMOVE_RECURSE "${EASYTIER_RUNTIME_DIR}")
file(ARCHIVE_EXTRACT INPUT "${_EASYTIER_ARCHIVE}" DESTINATION "${EASYTIER_CACHE_DIR}")
endif()
endif()

set(EASYTIER_AVAILABLE TRUE)
foreach(_file IN LISTS _EASYTIER_REQUIRED_FILES)
if(NOT EXISTS "${EASYTIER_RUNTIME_DIR}/${_file}")
set(EASYTIER_AVAILABLE FALSE)
endif()
endforeach()
if(NOT EXISTS "${EASYTIER_LICENSE}")
set(EASYTIER_AVAILABLE FALSE)
endif()

if(NOT EASYTIER_AVAILABLE)
if(EASYTIER_REQUIRED)
message(FATAL_ERROR
"The pinned EasyTier ${EASYTIER_VERSION} runtime is unavailable. "
"Set FETCH_EASYTIER=ON with network access, or provide the verified runtime in ${EASYTIER_RUNTIME_DIR}.")
endif()
message(WARNING "EasyTier is unavailable; Remote Connect will not be included in this package")
endif()

set(EASYTIER_AVAILABLE "${EASYTIER_AVAILABLE}" CACHE INTERNAL
"Whether the verified EasyTier runtime is available" FORCE)

if(EASYTIER_AVAILABLE AND TARGET sunshine)
foreach(_component IN ITEMS easytier-core.exe Packet.dll WinDivert64.sys wintun.dll)
file(SHA256 "${EASYTIER_RUNTIME_DIR}/${_component}" _component_sha256)
string(MAKE_C_IDENTIFIER "${_component}" _component_id)
string(TOUPPER "${_component_id}" _component_id)
target_compile_definitions(sunshine PRIVATE
"EASYTIER_${_component_id}_SHA256=\"${_component_sha256}\"")
endforeach()
endif()
10 changes: 10 additions & 0 deletions cmake/packaging/sunshine.iss.in
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,15 @@ Source: "{#MySourceDir}\tools\qiin-tabtip.exe"; DestDir: "{app}\tools"; Flags: i
Source: "{#MySourceDir}\tools\nefconw.exe"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: application
Source: "{#MySourceDir}\tools\stylus-input-probe.exe"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: application

; Remote Connect runtime
#if "@EASYTIER_AVAILABLE@" == "TRUE"
Source: "{#MySourceDir}\tools\easytier\easytier-core.exe"; DestDir: "{app}\tools\easytier"; Flags: ignoreversion; Components: application
Source: "{#MySourceDir}\tools\easytier\Packet.dll"; DestDir: "{app}\tools\easytier"; Flags: ignoreversion; Components: application
Source: "{#MySourceDir}\tools\easytier\WinDivert64.sys"; DestDir: "{app}\tools\easytier"; Flags: ignoreversion; Components: application
Source: "{#MySourceDir}\tools\easytier\wintun.dll"; DestDir: "{app}\tools\easytier"; Flags: ignoreversion; Components: application
Source: "{#MySourceDir}\tools\easytier\LICENSE-EasyTier.txt"; DestDir: "{app}\tools\easytier"; Flags: ignoreversion; Components: application
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
#endif

; Pinned manifest for the separately distributed first-party DualSense runtime.
Source: "{#MySourceDir}\tools\ds5-sidecar-package.json"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: application

Expand Down Expand Up @@ -322,6 +331,7 @@ Filename: "{app}\assets\gui\{#MyAppGUIExeName}"; Parameters: "--remove-autostart
; on its stop handler — which is the typical "uninstaller appears frozen" symptom.
Filename: "taskkill"; Parameters: "/f /im sunshine-gui.exe"; Flags: runhidden; RunOnceId: "KillGUI"
Filename: "taskkill"; Parameters: "/f /im sunshine.exe"; Flags: runhidden; RunOnceId: "KillSunshine"
Filename: "taskkill"; Parameters: "/f /im easytier-core.exe"; Flags: runhidden waituntilterminated; RunOnceId: "KillEasyTier"
Filename: "taskkill"; Parameters: "/f /im sunshinesvc.exe"; Flags: runhidden; RunOnceId: "KillSunshineSvc"
; Remove system components
Filename: "{app}\scripts\delete-firewall-rule.bat"; Flags: runhidden waituntilterminated; RunOnceId: "DelFirewall"
Expand Down
22 changes: 22 additions & 0 deletions cmake/packaging/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# Fetch driver dependencies (downloads at configure time)
include(${CMAKE_MODULE_PATH}/packaging/FetchDriverDeps.cmake)
include(${CMAKE_MODULE_PATH}/packaging/FetchEasyTier.cmake)

install(TARGETS sunshine RUNTIME DESTINATION "." COMPONENT application)

Expand All @@ -17,6 +18,27 @@ install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT application)
install(TARGETS qiin-tabtip RUNTIME DESTINATION "tools" COMPONENT application)
install(TARGETS stylus-input-probe RUNTIME DESTINATION "tools" COMPONENT application)

# Remote Connect runtime. Only the core and its Windows networking
# dependencies are installed; the EasyTier CLI/Web UI remain hidden from users.
if(EASYTIER_AVAILABLE)
install(FILES
"${EASYTIER_RUNTIME_DIR}/easytier-core.exe"
"${EASYTIER_RUNTIME_DIR}/Packet.dll"
"${EASYTIER_RUNTIME_DIR}/WinDivert64.sys"
"${EASYTIER_RUNTIME_DIR}/wintun.dll"
"${EASYTIER_LICENSE}"
DESTINATION "tools/easytier"
COMPONENT application)
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/tools/easytier")
file(COPY
"${EASYTIER_RUNTIME_DIR}/easytier-core.exe"
"${EASYTIER_RUNTIME_DIR}/Packet.dll"
"${EASYTIER_RUNTIME_DIR}/WinDivert64.sys"
"${EASYTIER_RUNTIME_DIR}/wintun.dll"
"${EASYTIER_LICENSE}"
DESTINATION "${CMAKE_BINARY_DIR}/tools/easytier")
endif()

# The optional self-contained runtime is a separate release asset. The main
# package carries only its pinned download manifest, keeping the feature
# installable without adding the complete .NET runtime to every Sunshine copy.
Expand Down
16 changes: 15 additions & 1 deletion src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,13 @@ namespace config {

10, // pair_max_attempts: default 10 attempts per IP per 60s

false, // remote_connect_enabled
{}, // remote_connect_profile
{}, // remote_connect_virtual_ip
{}, // remote_connect_network_name
{}, // remote_connect_network_secret
"udp://public.easytier.top:11010", // remote_connect_peer

true, // client_fingerprint_remote_rules
"https://raw.githubusercontent.com/AlkaidLab/sunshine-client-fingerprint-rules/main/stable.json",
{}, // client_fingerprint_rules_certificate (empty uses the pinned built-in certificate)
Expand Down Expand Up @@ -1191,7 +1198,8 @@ namespace config {

void apply_config(std::unordered_map<std::string, std::string> &&vars) {
for (auto &[name, val] : vars) {
const auto log_value = name == "file_mappings" && !val.empty() ? "<redacted>"s : val;
const bool sensitive = name == "file_mappings" || name == "remote_connect_network_secret";
const auto log_value = sensitive && !val.empty() ? "<redacted>"s : val;
BOOST_LOG(info) << "config: '"sv << name << "' = "sv << log_value;
modified_config_settings[name] = val;
}
Expand Down Expand Up @@ -1462,6 +1470,12 @@ namespace config {
list_string_f(vars, "fps"s, nvhttp.fps);
int_between_f(vars, "sleep_mode", nvhttp.sleep_mode, { SLEEP_MODE_SUSPEND, SLEEP_MODE_AWAY });
int_between_f(vars, "pair_max_attempts", nvhttp.pair_max_attempts, { 0, 50 });
bool_f(vars, "remote_connect_enabled", nvhttp.remote_connect_enabled);
string_f(vars, "remote_connect_profile", nvhttp.remote_connect_profile);
string_f(vars, "remote_connect_virtual_ip", nvhttp.remote_connect_virtual_ip);
string_f(vars, "remote_connect_network_name", nvhttp.remote_connect_network_name);
string_f(vars, "remote_connect_network_secret", nvhttp.remote_connect_network_secret);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
string_f(vars, "remote_connect_peer", nvhttp.remote_connect_peer);
bool_f(vars, "client_fingerprint_remote_rules", nvhttp.client_fingerprint_remote_rules);
string_f(vars, "client_fingerprint_rules_url", nvhttp.client_fingerprint_rules_url);
string_f(vars, "client_fingerprint_rules_certificate", nvhttp.client_fingerprint_rules_certificate);
Expand Down
9 changes: 9 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ namespace config {

int pair_max_attempts; // Max PIN pairing attempts per IP within 60s window. 0 disables limiting.

// Remote connection. The secret is generated locally and only leaves
// the host through an authenticated, short-lived pairing QR code.
bool remote_connect_enabled;
std::string remote_connect_profile;
std::string remote_connect_virtual_ip;
std::string remote_connect_network_name;
std::string remote_connect_network_secret;
std::string remote_connect_peer;

// Signed, warning-only client fingerprint rule feed.
bool client_fingerprint_remote_rules;
std::string client_fingerprint_rules_url;
Expand Down
Loading