Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -34,6 +34,11 @@ namespace crypto {
|
||||
using pkey_ctx_t = util::safe_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
using bignum_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||
|
||||
/**
|
||||
* @brief Hashes the given plaintext using SHA-256.
|
||||
* @param plaintext
|
||||
* @return The SHA-256 hash of the plaintext.
|
||||
*/
|
||||
sha256_t
|
||||
hash(const std::string_view &plaintext);
|
||||
|
||||
|
||||
@@ -109,6 +109,13 @@ namespace lifetime {
|
||||
}
|
||||
} // namespace lifetime
|
||||
|
||||
void
|
||||
log_publisher_data() {
|
||||
BOOST_LOG(info) << "Package Publisher: "sv << SUNSHINE_PUBLISHER_NAME;
|
||||
BOOST_LOG(info) << "Publisher Website: "sv << SUNSHINE_PUBLISHER_WEBSITE;
|
||||
BOOST_LOG(info) << "Get support: "sv << SUNSHINE_PUBLISHER_ISSUE_URL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool
|
||||
is_gamestream_enabled() {
|
||||
|
||||
@@ -108,6 +108,12 @@ namespace lifetime {
|
||||
get_argv();
|
||||
} // namespace lifetime
|
||||
|
||||
/**
|
||||
* @brief Log the publisher metadata provided from CMake.
|
||||
*/
|
||||
void
|
||||
log_publisher_data();
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
* @brief Check if NVIDIA's GameStream software is running.
|
||||
|
||||
@@ -114,6 +114,9 @@ main(int argc, char *argv[]) {
|
||||
// the version should be printed to the log before anything else
|
||||
BOOST_LOG(info) << PROJECT_NAME << " version: " << PROJECT_VER;
|
||||
|
||||
// Log publisher metadata
|
||||
log_publisher_data();
|
||||
|
||||
if (!config::sunshine.cmd.name.empty()) {
|
||||
auto fn = cmd_to_func.find(config::sunshine.cmd.name);
|
||||
if (fn == std::end(cmd_to_func)) {
|
||||
|
||||
@@ -206,4 +206,34 @@ namespace net {
|
||||
|
||||
return mapped_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a string for use as the instance name for mDNS.
|
||||
* @param hostname The hostname to use for instance name generation.
|
||||
* @return Hostname-based instance name or "Sunshine" if hostname is invalid.
|
||||
*/
|
||||
std::string
|
||||
mdns_instance_name(const std::string_view &hostname) {
|
||||
// Start with the unmodified hostname
|
||||
std::string instancename { hostname.data(), hostname.size() };
|
||||
|
||||
// Truncate to 63 characters per RFC 6763 section 7.2.
|
||||
if (instancename.size() > 63) {
|
||||
instancename.resize(63);
|
||||
}
|
||||
|
||||
for (auto i = 0; i < instancename.size(); i++) {
|
||||
// Replace any spaces with dashes
|
||||
if (instancename[i] == ' ') {
|
||||
instancename[i] = '-';
|
||||
}
|
||||
else if (!std::isalnum(instancename[i]) && instancename[i] != '-') {
|
||||
// Stop at the first invalid character
|
||||
instancename.resize(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return !instancename.empty() ? instancename : "Apollo";
|
||||
}
|
||||
} // namespace net
|
||||
|
||||
@@ -105,4 +105,12 @@ namespace net {
|
||||
*/
|
||||
int
|
||||
encryption_mode_for_address(boost::asio::ip::address address);
|
||||
|
||||
/**
|
||||
* @brief Returns a string for use as the instance name for mDNS.
|
||||
* @param hostname The hostname to use for instance name generation.
|
||||
* @return Hostname-based instance name or "Sunshine" if hostname is invalid.
|
||||
*/
|
||||
std::string
|
||||
mdns_instance_name(const std::string_view &hostname);
|
||||
} // namespace net
|
||||
|
||||
@@ -51,19 +51,38 @@ namespace nvhttp {
|
||||
static std::string otp_passphrase;
|
||||
static std::chrono::time_point<std::chrono::steady_clock> otp_creation_time;
|
||||
|
||||
class SunshineHttpsServer: public SimpleWeb::Server<SimpleWeb::HTTPS> {
|
||||
class SunshineHTTPS: public SimpleWeb::HTTPS {
|
||||
public:
|
||||
SunshineHttpsServer(const std::string &certification_file, const std::string &private_key_file):
|
||||
SimpleWeb::Server<SimpleWeb::HTTPS>::Server(certification_file, private_key_file) {}
|
||||
SunshineHTTPS(boost::asio::io_service &io_service, boost::asio::ssl::context &ctx):
|
||||
SimpleWeb::HTTPS(io_service, ctx) {}
|
||||
|
||||
virtual ~SunshineHTTPS() {
|
||||
// Gracefully shutdown the TLS connection
|
||||
SimpleWeb::error_code ec;
|
||||
shutdown(ec);
|
||||
}
|
||||
};
|
||||
|
||||
class SunshineHTTPSServer: public SimpleWeb::ServerBase<SunshineHTTPS> {
|
||||
public:
|
||||
SunshineHTTPSServer(const std::string &certification_file, const std::string &private_key_file):
|
||||
ServerBase<SunshineHTTPS>::ServerBase(443),
|
||||
context(boost::asio::ssl::context::tls_server) {
|
||||
// Disabling TLS 1.0 and 1.1 (see RFC 8996)
|
||||
context.set_options(boost::asio::ssl::context::no_tlsv1);
|
||||
context.set_options(boost::asio::ssl::context::no_tlsv1_1);
|
||||
context.use_certificate_chain_file(certification_file);
|
||||
context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);
|
||||
}
|
||||
|
||||
std::function<int(SSL *)> verify;
|
||||
std::function<void(std::shared_ptr<Response>, std::shared_ptr<Request>)> on_verify_failed;
|
||||
|
||||
protected:
|
||||
boost::asio::ssl::context context;
|
||||
|
||||
void
|
||||
after_bind() override {
|
||||
SimpleWeb::Server<SimpleWeb::HTTPS>::after_bind();
|
||||
|
||||
if (verify) {
|
||||
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once);
|
||||
context.set_verify_callback([](int verified, boost::asio::ssl::verify_context &ctx) {
|
||||
@@ -115,7 +134,7 @@ namespace nvhttp {
|
||||
}
|
||||
};
|
||||
|
||||
using https_server_t = SunshineHttpsServer;
|
||||
using https_server_t = SunshineHTTPSServer;
|
||||
using http_server_t = SimpleWeb::Server<SimpleWeb::HTTP>;
|
||||
|
||||
struct conf_intern_t {
|
||||
@@ -150,7 +169,7 @@ namespace nvhttp {
|
||||
struct {
|
||||
util::Either<
|
||||
std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>,
|
||||
std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>>
|
||||
std::shared_ptr<typename SimpleWeb::ServerBase<SunshineHTTPS>::Response>>
|
||||
response;
|
||||
std::string salt;
|
||||
} async_insert_pin;
|
||||
@@ -162,8 +181,8 @@ namespace nvhttp {
|
||||
std::atomic<uint32_t> session_id_counter;
|
||||
|
||||
using args_t = SimpleWeb::CaseInsensitiveMultimap;
|
||||
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
|
||||
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SunshineHTTPS>::Response>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SunshineHTTPS>::Request>;
|
||||
using resp_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>;
|
||||
using req_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Request>;
|
||||
|
||||
@@ -496,7 +515,7 @@ namespace nvhttp {
|
||||
struct tunnel;
|
||||
|
||||
template <>
|
||||
struct tunnel<SimpleWeb::HTTPS> {
|
||||
struct tunnel<SunshineHTTPS> {
|
||||
static auto constexpr to_string = "HTTPS"sv;
|
||||
};
|
||||
|
||||
@@ -715,7 +734,7 @@ namespace nvhttp {
|
||||
print_req<T>(request);
|
||||
|
||||
int pair_status = 0;
|
||||
if constexpr (std::is_same_v<SimpleWeb::HTTPS, T>) {
|
||||
if constexpr (std::is_same_v<SunshineHTTPS, T>) {
|
||||
auto args = request->parse_query_string();
|
||||
auto clientID = args.find("uniqueid"s);
|
||||
|
||||
@@ -745,7 +764,7 @@ namespace nvhttp {
|
||||
|
||||
// Only include the MAC address for requests sent from paired clients over HTTPS.
|
||||
// For HTTP requests, use a placeholder MAC address that Moonlight knows to ignore.
|
||||
if constexpr (std::is_same_v<SimpleWeb::HTTPS, T>) {
|
||||
if constexpr (std::is_same_v<SunshineHTTPS, T>) {
|
||||
tree.put("root.mac", platf::get_mac_address(net::addr_to_normalized_string(local_endpoint.address())));
|
||||
}
|
||||
else {
|
||||
@@ -826,7 +845,7 @@ namespace nvhttp {
|
||||
|
||||
void
|
||||
applist(resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
print_req<SunshineHTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
|
||||
@@ -855,7 +874,7 @@ namespace nvhttp {
|
||||
|
||||
void
|
||||
launch(bool &host_audio, resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
print_req<SunshineHTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
@@ -952,7 +971,7 @@ namespace nvhttp {
|
||||
|
||||
void
|
||||
resume(bool &host_audio, resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
print_req<SunshineHTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
@@ -1038,7 +1057,7 @@ namespace nvhttp {
|
||||
|
||||
void
|
||||
cancel(resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
print_req<SunshineHTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
@@ -1069,7 +1088,7 @@ namespace nvhttp {
|
||||
|
||||
void
|
||||
appasset(resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
print_req<SunshineHTTPS>(request);
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
auto app_image = proc::proc.get_app_image(util::from_view(get_arg(args, "appid")));
|
||||
@@ -1162,9 +1181,9 @@ namespace nvhttp {
|
||||
tree.put("root.<xmlattr>.status_message"s, "The client is not authorized. Certificate verification failed."s);
|
||||
};
|
||||
|
||||
https_server.default_resource["GET"] = not_found<SimpleWeb::HTTPS>;
|
||||
https_server.resource["^/serverinfo$"]["GET"] = serverinfo<SimpleWeb::HTTPS>;
|
||||
https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SimpleWeb::HTTPS>(add_cert, resp, req); };
|
||||
https_server.default_resource["GET"] = not_found<SunshineHTTPS>;
|
||||
https_server.resource["^/serverinfo$"]["GET"] = serverinfo<SunshineHTTPS>;
|
||||
https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SunshineHTTPS>(add_cert, resp, req); };
|
||||
https_server.resource["^/applist$"]["GET"] = applist;
|
||||
https_server.resource["^/appasset$"]["GET"] = appasset;
|
||||
https_server.resource["^/launch$"]["GET"] = [&host_audio](auto resp, auto req) { launch(host_audio, resp, req); };
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include <string>
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#ifndef _WIN32
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/process.hpp>
|
||||
#endif
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
@@ -31,6 +35,7 @@ struct AVHWFramesContext;
|
||||
struct AVCodecContext;
|
||||
struct AVDictionary;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Forward declarations of boost classes to avoid having to include boost headers
|
||||
// here, which results in issues with Windows.h and WinSock2.h include order.
|
||||
namespace boost {
|
||||
@@ -50,6 +55,7 @@ namespace boost {
|
||||
typedef basic_environment<char> environment;
|
||||
} // namespace process
|
||||
} // namespace boost
|
||||
#endif
|
||||
namespace video {
|
||||
struct config_t;
|
||||
} // namespace video
|
||||
@@ -612,6 +618,23 @@ namespace platf {
|
||||
void
|
||||
restart();
|
||||
|
||||
/**
|
||||
* @brief Set an environment variable.
|
||||
* @param name The name of the environment variable.
|
||||
* @param value The value to set the environment variable to.
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int
|
||||
set_env(const std::string &name, const std::string &value);
|
||||
|
||||
/**
|
||||
* @brief Unset an environment variable.
|
||||
* @param name The name of the environment variable.
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int
|
||||
unset_env(const std::string &name);
|
||||
|
||||
struct buffer_descriptor_t {
|
||||
const char *buffer;
|
||||
size_t size;
|
||||
@@ -837,6 +860,8 @@ namespace platf {
|
||||
|
||||
/**
|
||||
* @brief Gets the supported gamepads for this platform backend.
|
||||
* @details This may be called prior to `platf::input()`!
|
||||
* @param input Pointer to the platform's `input_t` or `nullptr`.
|
||||
* @return Vector of gamepad options and status.
|
||||
*/
|
||||
std::vector<supported_gamepad_t> &
|
||||
|
||||
@@ -326,6 +326,16 @@ namespace platf {
|
||||
lifetime::exit_sunshine(0, true);
|
||||
}
|
||||
|
||||
int
|
||||
set_env(const std::string &name, const std::string &value) {
|
||||
return setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
|
||||
int
|
||||
unset_env(const std::string &name) {
|
||||
return unsetenv(name.c_str());
|
||||
}
|
||||
|
||||
bool
|
||||
request_process_group_exit(std::uintptr_t native_handle) {
|
||||
if (kill(-((pid_t) native_handle), SIGTERM) == 0 || errno == ESRCH) {
|
||||
@@ -913,6 +923,10 @@ namespace platf {
|
||||
|
||||
std::unique_ptr<deinit_t>
|
||||
init() {
|
||||
// enable low latency mode for AMD
|
||||
// https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30039
|
||||
set_env("AMD_DEBUG", "lowlatency");
|
||||
|
||||
// These are allowed to fail.
|
||||
gbm::init();
|
||||
|
||||
|
||||
@@ -426,7 +426,8 @@ namespace platf::publish {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
name.reset(avahi::strdup(SERVICE_NAME));
|
||||
auto instance_name = net::mdns_instance_name(boost::asio::ip::host_name());
|
||||
name.reset(avahi::strdup(instance_name.c_str()));
|
||||
|
||||
client.reset(
|
||||
avahi::client_new(avahi::simple_poll_get(poll.get()), avahi::ClientFlags(0), client_callback, nullptr, &avhi_error));
|
||||
|
||||
@@ -253,6 +253,16 @@ namespace platf {
|
||||
lifetime::exit_sunshine(0, true);
|
||||
}
|
||||
|
||||
int
|
||||
set_env(const std::string &name, const std::string &value) {
|
||||
return setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
|
||||
int
|
||||
unset_env(const std::string &name) {
|
||||
return unsetenv(name.c_str());
|
||||
}
|
||||
|
||||
bool
|
||||
request_process_group_exit(std::uintptr_t native_handle) {
|
||||
if (killpg((pid_t) native_handle, SIGTERM) == 0 || errno == ESRCH) {
|
||||
|
||||
@@ -105,7 +105,8 @@ namespace platf::publish {
|
||||
&serviceRef,
|
||||
0, // flags
|
||||
0, // interfaceIndex
|
||||
SERVICE_NAME, SERVICE_TYPE,
|
||||
nullptr, // name
|
||||
SERVICE_TYPE,
|
||||
nullptr, // domain
|
||||
nullptr, // host
|
||||
htons(net::map_port(nvhttp::PORT_HTTP)),
|
||||
|
||||
@@ -1728,15 +1728,18 @@ namespace platf {
|
||||
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
bool enabled;
|
||||
if (input) {
|
||||
auto vigem = ((input_raw_t *) input)->vigem;
|
||||
enabled = vigem != nullptr;
|
||||
}
|
||||
else {
|
||||
enabled = false;
|
||||
if (!input) {
|
||||
static std::vector gps {
|
||||
supported_gamepad_t { "auto", true, "" },
|
||||
supported_gamepad_t { "x360", false, "" },
|
||||
supported_gamepad_t { "ds4", false, "" },
|
||||
};
|
||||
|
||||
return gps;
|
||||
}
|
||||
|
||||
auto vigem = ((input_raw_t *) input)->vigem;
|
||||
auto enabled = vigem != nullptr;
|
||||
auto reason = enabled ? "" : "gamepads.vigem-not-available";
|
||||
|
||||
// ds4 == ps4
|
||||
|
||||
@@ -1313,6 +1313,16 @@ namespace platf {
|
||||
lifetime::exit_sunshine(0, true);
|
||||
}
|
||||
|
||||
int
|
||||
set_env(const std::string &name, const std::string &value) {
|
||||
return _putenv_s(name.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
int
|
||||
unset_env(const std::string &name) {
|
||||
return _putenv_s(name.c_str(), "");
|
||||
}
|
||||
|
||||
struct enum_wnd_context_t {
|
||||
std::set<DWORD> process_ids;
|
||||
bool requested_exit;
|
||||
|
||||
@@ -37,7 +37,6 @@ constexpr auto DNS_QUERY_RESULTS_VERSION1 = 0x1;
|
||||
|
||||
#define SERVICE_DOMAIN "local"
|
||||
|
||||
constexpr auto SERVICE_INSTANCE_NAME = SV(SERVICE_NAME "." SERVICE_TYPE "." SERVICE_DOMAIN);
|
||||
constexpr auto SERVICE_TYPE_DOMAIN = SV(SERVICE_TYPE "." SERVICE_DOMAIN);
|
||||
|
||||
#ifndef __MINGW32__
|
||||
@@ -107,10 +106,11 @@ namespace platf::publish {
|
||||
service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) {
|
||||
auto alarm = safe::make_alarm<PDNS_SERVICE_INSTANCE>();
|
||||
|
||||
std::wstring name { SERVICE_INSTANCE_NAME.data(), SERVICE_INSTANCE_NAME.size() };
|
||||
std::wstring domain { SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size() };
|
||||
|
||||
auto host = from_utf8(boost::asio::ip::host_name() + ".local");
|
||||
auto hostname = boost::asio::ip::host_name();
|
||||
auto name = from_utf8(net::mdns_instance_name(hostname) + '.') + domain;
|
||||
auto host = from_utf8(hostname + ".local");
|
||||
|
||||
DNS_SERVICE_INSTANCE instance {};
|
||||
instance.pszInstanceName = name.data();
|
||||
|
||||
Reference in New Issue
Block a user