Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@ 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/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/service.cpp"
"${CMAKE_SOURCE_DIR}/src/remote_connect/service.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
Loading