diff --git a/CMakeLists.txt b/CMakeLists.txt index c81733ea..8f0c452e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ set(SUNSHINE_TARGET_FILES sunshine/platform/common.h sunshine/process.cpp sunshine/process.h + sunshine/network.cpp + sunshine/network.h sunshine/move_by_copy.h sunshine/task_pool.h sunshine/thread_pool.h diff --git a/Simple-Web-Server b/Simple-Web-Server index e70509e0..f4b7101a 160000 --- a/Simple-Web-Server +++ b/Simple-Web-Server @@ -1 +1 @@ -Subproject commit e70509e078ade2eb9679862ab6a48b4ee2fefb66 +Subproject commit f4b7101ad369f56423291d6ac39ebf61a6518298 diff --git a/assets/sunshine.conf b/assets/sunshine.conf index dbdbaac7..b5a0764e 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -10,6 +10,15 @@ # The name displayed by Moonlight # sunshine_name = sunshine +# The origin of the remote endpoint address that is not denied for HTTP method /pin +# Could be any of the following values: +# pc|lan|wan +# pc: Only localhost may access /pin +# lan: Only those in LAN may access /pin +# wan: Anyone may access /pin +# +# origin_pin_allowed = lan + # Pretty self-explanatory unique_id = 03904e64-51da-4fb3-9afd-a9f7ff70fea4 @@ -25,7 +34,7 @@ ping_timeout = 2000 # How much error correcting packets must be send for every video max_b_frames # This is just some random number, don't know the optimal value # The higher fec_percentage, the lower space for the actual data to send per frame there is -fec_percentage = 1 +fec_percentage = 10 # The back/select button on the controller @@ -39,7 +48,7 @@ fec_percentage = 1 # FFmpeg software encoding parameters # Honestly, I have no idea what the optimal values would be. # Play around with this :) -max_b_frames = 16 +max_b_frames = 4 gop_size = 24 # Constant Rate Factor. Between 1 and 52. It allows QP to go up during motion and down with still image, resulting in constant perceived quality diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 2472500c..31aa9b29 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -36,12 +36,13 @@ stream_t stream { }; nvhttp_t nvhttp { + "lan", // origin_pin PRIVATE_KEY_FILE, CERTIFICATE_FILE, - "sunshine", // sunshine_name - "03904e64-51da-4fb3-9afd-a9f7ff70fea4", // unique_id - "devices.json" // file_devices + "sunshine"s, // sunshine_name, + "03904e64-51da-4fb3-9afd-a9f7ff70fea4"s, // unique_id + "devices.json"s // file_devices }; input_t input { @@ -102,6 +103,32 @@ void string_f(std::unordered_map &vars, const std::str input = std::move(it->second); } +void string_restricted_f(std::unordered_map &vars, const std::string &name, std::string &input, const std::vector &allowed_vals) { + std::string temp; + string_f(vars, name, temp); + + for(auto &allowed_val : allowed_vals) { + if(temp == allowed_val) { + input = std::move(temp); + return; + } + } +} + +void int_restricted_f(std::unordered_map &vars, const std::string &name, int &input, const std::vector &allowed_vals) { + std::string temp; + string_f(vars, name, temp); + + for(int x = 0; x < allowed_vals.size(); ++x) { + auto &allowed_val = allowed_vals[x]; + + if(temp == allowed_val) { + input = x; + return; + } + } +} + void int_f(std::unordered_map &vars, const std::string &name, int &input) { auto it = vars.find(name); @@ -142,6 +169,10 @@ void parse_file(const char *file) { string_f(vars, "file_devices", nvhttp.file_devices); string_f(vars, "external_ip", nvhttp.external_ip); + string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { + "pc"sv, "lan"sv, "wan"sv + }); + int to = -1; int_f(vars, "ping_timeout", to); if(to > 0) { diff --git a/sunshine/config.h b/sunshine/config.h index 9b0e870c..521eeabb 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -28,6 +28,10 @@ struct stream_t { }; struct nvhttp_t { + // Could be any of the following values: + // pc|lan|wan + std::string origin_pin_allowed; + std::string pkey; // must be 2048 bits std::string cert; // must be signed with a key of 2048 bits diff --git a/sunshine/network.cpp b/sunshine/network.cpp new file mode 100644 index 00000000..f49c9af8 --- /dev/null +++ b/sunshine/network.cpp @@ -0,0 +1,96 @@ +// +// Created by loki on 12/27/19. +// + +#include +#include "network.h" +#include "utility.h" + +namespace net { +using namespace std::literals; +// In the format "xxx.xxx.xxx.xxx/x" +std::pair ip_block(const std::string_view &ip); + +std::vector> pc_ips { + ip_block("127.0.0.1/32"sv) +}; +std::vector> lan_ips { + ip_block("192.168.0.0/16"sv), + ip_block("172.16.0.0/12"), + ip_block("10.0.0.0/8"sv) +}; + +std::uint32_t ip(const std::string_view &ip_str) { + auto begin = std::begin(ip_str); + auto end = std::end(ip_str); + auto temp_end = std::find(begin, end, '.'); + + std::uint32_t ip = 0; + auto shift = 24; + while(temp_end != end) { + ip += (util::from_chars(begin, temp_end) << shift); + shift -= 8; + + begin = temp_end + 1; + temp_end = std::find(begin, end, '.'); + } + + ip += util::from_chars(begin, end); + + return ip; +} + +// In the format "xxx.xxx.xxx.xxx/x" +std::pair ip_block(const std::string_view &ip_str) { + auto begin = std::begin(ip_str); + auto end = std::find(begin, std::end(ip_str), '/'); + + auto addr = ip({ begin, (std::size_t)(end - begin) }); + + auto bits = 32 - util::from_chars(end + 1, std::end(ip_str)); + + return { addr, addr + ((1 << bits) - 1) }; +} + +net_e from_enum_string(const std::string_view &view) { + if(view == "wan") { + return WAN; + } + if(view == "lan") { + return LAN; + } + + return PC; +} +net_e from_address(const std::string_view &view) { + auto addr = ip(view); + + for(auto [ip_low, ip_high] : pc_ips) { + if(addr >= ip_low && addr <= ip_high) { + return PC; + } + } + + for(auto [ip_low, ip_high] : lan_ips) { + if(addr >= ip_low && addr <= ip_high) { + return LAN; + } + } + + return WAN; +} + +std::string_view to_enum_string(net_e net) { + switch(net) { + case PC: + return "pc"sv; + case LAN: + return "lan"sv; + case WAN: + return "wan"sv; + } + + // avoid warning + return "wan"sv; +} +} \ No newline at end of file diff --git a/sunshine/network.h b/sunshine/network.h new file mode 100644 index 00000000..8ddb8aef --- /dev/null +++ b/sunshine/network.h @@ -0,0 +1,22 @@ +// +// Created by loki on 12/27/19. +// + +#ifndef SUNSHINE_NETWORK_H +#define SUNSHINE_NETWORK_H + +#include +namespace net { +enum net_e : int { + PC, + LAN, + WAN +}; + +net_e from_enum_string(const std::string_view &view); +std::string_view to_enum_string(net_e net); + +net_e from_address(const std::string_view &view); +} + +#endif //SUNSHINE_NETWORK_H diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index ede9a5d7..47f9e48b 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -22,6 +22,7 @@ #include "nvhttp.h" #include "platform/common.h" #include "process.h" +#include "network.h" namespace nvhttp { @@ -37,7 +38,6 @@ namespace pt = boost::property_tree; std::string read_file(const char *path); -std::string local_ip; using https_server_t = SimpleWeb::Server; using http_server_t = SimpleWeb::Server; @@ -88,6 +88,8 @@ enum class op_e { }; std::int64_t current_appid { -1 }; +std::string local_ip; +net::net_e origin_pin_allowed; void save_devices() { pt::ptree root; @@ -105,7 +107,7 @@ void save_devices() { cert_nodes.push_back(std::make_pair(""s, cert_node)); } node.add_child("certs"s, cert_nodes); - + nodes.push_back(std::make_pair(""s, node)); } @@ -374,6 +376,16 @@ template void pin(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { print_req(request); + auto address = request->remote_endpoint_address(); + auto ip_type = net::from_address(address); + if(ip_type > origin_pin_allowed) { + std::cout << '[' << address << "] -- denied"sv << std::endl; + + response->write(SimpleWeb::StatusCode::client_error_forbidden); + + return; + } + pt::ptree tree; auto &sess = std::begin(map_id_sess)->second; @@ -384,16 +396,22 @@ void pin(std::shared_ptr::Response> response, pt::write_xml(data, tree); auto &async_response = sess.async_insert_pin.response; - if(async_response.left()) { + if(async_response.has_left() && async_response.left()) { async_response.left()->write(data.str()); } - else { + else if(async_response.has_right() && async_response.right()){ async_response.right()->write(data.str()); } + else { + response->write(SimpleWeb::StatusCode::client_error_im_a_teapot); + return; + } + + // reset async_response async_response = std::decay_t(); // response to the current request - response->write(""s); + response->write(SimpleWeb::StatusCode::success_ok); } template @@ -631,6 +649,8 @@ void appasset(resp_https_t response, req_https_t request) { void start() { local_ip = platf::get_local_ip(); + origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed); + if(local_ip.empty()) { std::cout << "Error: Could not determine the local ip-address"sv << std::endl;