diff --git a/ServerPixel/ServerPixel.cpp b/ServerPixel/ServerPixel.cpp index 2ff5668..3e8ee60 100644 --- a/ServerPixel/ServerPixel.cpp +++ b/ServerPixel/ServerPixel.cpp @@ -1,6 +1,10 @@ // websocket #include +#include #include +#include +#include +#include #include #include #include @@ -40,10 +44,14 @@ // file read/write #include +// openssl +#define BOOST_NETWORK_ENABLE_HTTPS + namespace beast = boost::beast; namespace http = beast::http; namespace websocket = beast::websocket; namespace net = boost::asio; +namespace ssl = boost::asio::ssl; using tcp = boost::asio::ip::tcp; @@ -135,6 +143,8 @@ void LOG(std::stringstream&& s, WORD color) } +void do_session(tcp::socket sock, ssl::context& ctx, sessionManager& sessManager, std::string ip, uint64_t& user_n, std::string appPath, std::string appArgs, uint16_t portStream, uint16_t portHttp); + int main(int argc, char* argv[]) { // disable console input @@ -166,7 +176,7 @@ int main(int argc, char* argv[]) } } } - + // args // 1 - copies amount // 2 - server ip @@ -175,30 +185,33 @@ int main(int argc, char* argv[]) // 5 - start_http_port // 6 - start_streamer_port - // 7 - path_to_application - // 8 - application_args (without -pixelStreamingPort cos its argv[3]) + // 7 - path to crt + // 8 - path to key + // 9 - path to pem + + // 10 - path_to_application + // 11 - application_args (without -pixelStreamingPort cos its argv[3]) // sessions uint16_t sessionLimit = std::atoi(argv[1]); sessionManager sessManager; sessManager.setlimit(sessionLimit); + // user count + uint64_t user_n = 0; + // merging app arguments - std::string appPath = argv[7]; + std::string appPath = argv[10]; std::string appArgs; - for (int i = 8; i < argc; ++i) + for (int i = 11; i < argc; ++i) appArgs += argv[i] + std::string(" "); - appArgs += "-PixelStreamingPort="; + appArgs += "-PixelStreamingPort="; // first app ports uint16_t portHttp = std::atoi(argv[5]); uint16_t portStream = std::atoi(argv[6]); - // node js - /*std::string nodePath = "C:\\Program Files\\nodejs\\node.exe"; - std::string nodeFilePath = argv[2];*/ - // starting node js servers for (uint16_t i = 0; i < sessionLimit; ++i) { @@ -209,297 +222,331 @@ int main(int argc, char* argv[]) std::string ip(argv[2]); LOG((std::stringstream() << "ip: " << ip), 7); auto const address = net::ip::make_address(ip); - auto const port = static_cast(std::atoi(argv[3])); + auto const port = static_cast(std::atoi(argv[3])); LOG((std::stringstream() << "port: " << port), 7); - net::io_context ioc{ 1 }; - tcp::acceptor acceptor{ ioc, {address, port} }; - // stats count - uint64_t user_n = 0; + try + { + // the io_context is required for all I/O + net::io_context ioc{ 1 }; + + // the SSL context is required, and holds certificates + ssl::context ctx{ ssl::context::tlsv12 }; + + //// loading certificate, key, pem + ctx.use_certificate_chain_file(argv[7]); + ctx.use_private_key_file(argv[8], boost::asio::ssl::context::file_format::pem); + ctx.use_tmp_dh_file(argv[9]); + + // the acceptor receives incoming connections + tcp::acceptor acceptor{ ioc, {address, port} }; + + while (true) + { + // This will receive the new connection + tcp::socket socket(ioc); + // Block until we get a connection + acceptor.accept(socket); + + // Launch the session, transferring ownership of the socket + std::thread + { + &do_session, + std::move(socket), + std::ref(ctx), + std::ref(sessManager), + ip, + std::ref(user_n), + appPath, + appArgs, + portStream, + portHttp + }.detach(); + } + } + catch (const std::exception& e) + { + LOG(std::stringstream() << "Error: " << e.what(), 12); + return EXIT_FAILURE; + } + + logFile.close(); + return 0; +} + +void do_session(tcp::socket sock, ssl::context& ctx, sessionManager& sessManager, std::string ip, uint64_t& user_n, std::string appPath, std::string appArgs, uint16_t portStream, uint16_t portHttp) +{ + std::string user_ip = sock.remote_endpoint().address().to_string(); + uint64_t user_id = std::chrono::system_clock::now().time_since_epoch().count(); + user_id = hash6(user_ip + std::to_string(user_id)); + uint64_t thisUserNum = ++user_n; + LOG((std::stringstream() << "user(" << thisUserNum << ") connected (ip: " << user_ip << "), (id: " << user_id << ")"), 7); + + // Construct the websocket stream around the socket + websocket::stream> ws(sock, ctx); + //websocket::stream ws{ std::move(sock) }; + + try + { + // Perform the SSL handshake + ws.next_layer().handshake(ssl::stream_base::server); + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-sync-ssl"); + })); + + ws.accept(); + } + catch (const std::exception& e) + { + LOG(std::stringstream() << "Error: " << e.what(), 12); + return; + } + catch (beast::system_error const& se) + { + LOG(std::stringstream() << "Error: " << se.what(), 12); + return; + } + + nlohmann::json jsonData; + std::string message; + std::string content; + + //session sessTmp; + session* sessCur = nullptr; + uint32_t sessId = 0; while (true) { - tcp::socket socket(ioc); - acceptor.accept(socket); - - std::thread([ - sock = std::move(socket), - &sessManager, - &ip, - &user_n, - appPath, - appArgs, - portStream, - portHttp - //nodePath, - //nodeFilePath - ]() mutable + try { - std::string user_ip = sock.remote_endpoint().address().to_string(); - uint64_t user_id = std::chrono::system_clock::now().time_since_epoch().count(); - user_id = hash6(user_ip + std::to_string(user_id)); + // This buffer will hold the incoming message + // buffer types https://www.boost.org/doc/libs/1_75_0/libs/beast/doc/html/beast/using_io/buffer_types.html + // check for the best one + // beast::multi_buffer buffer; + beast::flat_buffer buffer; - uint64_t thisUserNum = ++user_n; - LOG((std::stringstream() << "user(" << thisUserNum << ") connected (ip: " << user_ip << "), (id: " << user_id << ")"), 7); + // Read a message + ws.read(buffer); - // init websocket - websocket::stream ws(std::move(const_cast(sock))); - ws.accept(); - /*try + // cout buffer to string + //std::cout << beast::buffers_to_string(buffer.data()) << std::endl; + + // validate errors + try { - ws.accept(); + jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data())); + message = jsonData.at("message"); } - catch (std::exception const& e) + catch (nlohmann::json::parse_error& e) { - std::cerr << "Error: " << e.what() << std::endl; - }*/ - // Set a decorator to change the Server of the - /*ws.set_option(websocket::stream_base::decorator( - [](websocket::response_type& res) - { - res.set(http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-server-sync"); - }));*/ - - nlohmann::json jsonData; - std::string message; - std::string content; - - - - //session sessTmp; - session* sessCur = nullptr; - uint32_t sessId = 0; - - while (true) + std::cout << "Parse error:" << e.what() << std::endl; + } + catch (nlohmann::json::out_of_range& e) { - try + std::cout << "Out of range:" << e.what() << std::endl; + } + catch (nlohmann::json::exception& e) + { + std::cout << "JSON exception: " << e.what() << std::endl; + } + catch (std::exception& e) + { + std::cout << "Unknown exception" << e.what() << std::endl; + } + + + + + // validate message + if (message == "NEW_USER") + { + buffer.clear(); + boost::beast::ostream(buffer) << "connected"; + ws.write(buffer.data()); + + } + else if (message == "NEW_SESS") + { + buffer.clear(); + + if (sessManager.isLimitReached()) { - // This buffer will hold the incoming message - // buffer types https://www.boost.org/doc/libs/1_75_0/libs/beast/doc/html/beast/using_io/buffer_types.html - // check for the best one - // beast::multi_buffer buffer; - beast::flat_buffer buffer; + boost::beast::ostream(buffer) + << std::string("{\"message\" : \"SESS_LIMIT\"}").c_str(); + ws.write(buffer.data()); + continue; + } + if (sessManager.existsOwnerId(user_id)) + { + boost::beast::ostream(buffer) + << std::string("{\"message\" : \"PERSON_LIMIT\"}").c_str(); + ws.write(buffer.data()); + continue; + } - // Read a message - ws.read(buffer); - // cout buffer to string - //std::cout << beast::buffers_to_string(buffer.data()) << std::endl; - - // validate errors - try + // get session port + //uint16_t sessMinPort = sessManager.getMinPort(portHttp); + uint16_t sessHttpPort = sessManager.getMinPort(portHttp);//(sessMinPort == 0) ? portHttp : sessMinPort + 1; + uint16_t sessStreamPort = portStream + (sessHttpPort - portHttp); + + + // creating process + PROCESS_INFORMATION appProcInfo; + + if (!CreateProc(appPath, appPath + " " + appArgs + std::to_string(sessStreamPort), &appProcInfo)) + { + LOG((std::stringstream() << "CreateProcess failed (" << GetLastError() << ")."), 12); + continue; + } + + boost::beast::ostream(buffer) + << std::string("{\"message\" : \"CREATING_SESSION\", \"content\" : \"true\"}").c_str(); + ws.write(buffer.data()); + + + + // bind app process close listen + std::thread([appProcInfo]() { - jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data())); - message = jsonData.at("message"); - } - catch (nlohmann::json::parse_error& e) - { - std::cout << "Parse error:" << e.what() << std::endl; - } - catch (nlohmann::json::out_of_range& e) - { - std::cout << "Out of range:" << e.what() << std::endl; - } - catch (nlohmann::json::exception& e) - { - std::cout << "JSON exception: " << e.what() << std::endl; - } - catch (std::exception& e) - { - std::cout << "Unknown exception" << e.what() << std::endl; - } + WaitForSingleObject(appProcInfo.hProcess, INFINITE); + CloseHandle(appProcInfo.hProcess); + CloseHandle(appProcInfo.hThread); + }).detach(); - + sessId = hash6(user_id); - - // validate message - if (message == "NEW_USER") - { - buffer.clear(); - boost::beast::ostream(buffer) << "connected"; - ws.write(buffer.data()); - - } - else if (message == "NEW_SESS") - { - buffer.clear(); - - if (sessManager.isLimitReached()) - { - boost::beast::ostream(buffer) - << std::string("{\"message\" : \"SESS_LIMIT\"}").c_str(); - ws.write(buffer.data()); - continue; - } - if (sessManager.existsOwnerId(user_id)) - { - boost::beast::ostream(buffer) - << std::string("{\"message\" : \"PERSON_LIMIT\"}").c_str(); - ws.write(buffer.data()); - continue; - } - - - // get session port - uint16_t sessMinPort = sessManager.getMinPort(); - if (!sessMinPort) - sessMinPort = portHttp; - uint16_t sessHttpPort = sessMinPort + 1; - uint16_t sessStreamPort = portStream + (sessHttpPort - portHttp); - - - // creating process - PROCESS_INFORMATION appProcInfo; - - if (!CreateProc(appPath, appPath + " " + appArgs + std::to_string(sessStreamPort), &appProcInfo)) - { - LOG((std::stringstream() << "CreateProcess failed (" << GetLastError() << ")."), 12); - continue; - } - - boost::beast::ostream(buffer) - << std::string("{\"message\" : \"CREATING_SESSION\", \"content\" : \"true\"}").c_str(); - ws.write(buffer.data()); - - - - // bind app process close listen - std::thread([appProcInfo]() - { - WaitForSingleObject(appProcInfo.hProcess, INFINITE); - CloseHandle(appProcInfo.hProcess); - CloseHandle(appProcInfo.hThread); - }).detach(); - - sessId = hash6(user_id); - - // create session - sessManager.add( - session( + // create session + sessManager.add( + session( sessId, ip + std::to_string(sessHttpPort), user_ip, sessHttpPort )); - - if (sessManager.getById(sessId, &sessCur)) - { - // add user - sessCur->addUser(user(user_id)); - // add process ids - sessCur->addProcId(GetProcessId(appProcInfo.hProcess)); - //sessCur->addProcId(GetProcessId(nodeProcInfo.hProcess)); - } - - LOG((std::stringstream() << "session [" - << sessId << "] created, (httpPort: " << sessHttpPort << "), (streamPort: " << std::to_string(sessStreamPort) + ")"), 10); - - // send respond - buffer.clear(); - boost::beast::ostream(buffer) - << (std::string("{\"message\" : \"SESS_CREATION\", \"id\" : \"") - + std::to_string(sessId) + "\", \"port\" : \"" + std::to_string(sessHttpPort) + "\"}"); - ws.write(buffer.data()); - } - else if (message == "SESS_CONNECT") + if (sessManager.getById(sessId, &sessCur)) { - // read session id - try - { - jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data())); - content = jsonData.at("content"); - } - catch (nlohmann::json::parse_error& e) - { - std::cout << "Parse error:" << e.what() << std::endl; - } - catch (nlohmann::json::out_of_range& e) - { - std::cout << "Out of range:" << e.what() << std::endl; - } - catch (nlohmann::json::exception& e) - { - std::cout << "JSON exception: " << e.what() << std::endl; - } - catch (std::exception& e) - { - std::cout << "Unknown exception" << e.what() << std::endl; - } + // add user + sessCur->addUser(user(user_id)); - sessId = atoi(content.c_str()); - // connect to session - if (sessManager.getById(sessId, &sessCur)) - { - if (sessCur->existsUserId(user_id)) - { - LOG((std::stringstream() << "user exists: " << user_id), 7); - continue; - } - sessCur->addUser(user(user_id)); - LOG((std::stringstream() << "user(" << thisUserNum << ") connected to session (id: " << sessCur->getId() - << "), (users: " << sessCur->getUsersCount() << ")"), 14); - - buffer.clear(); - boost::beast::ostream(buffer) - << (std::string("{\"message\" : \"SESS_CONNECT\", \"content\" : \"") + std::to_string(sessCur->getPort()) + "\"}"); - ws.write(buffer.data()); - } - else - { - LOG((std::stringstream() << "no session (id: " << content << ")"), 14); - buffer.clear(); - boost::beast::ostream(buffer) - << (std::string("{\"message\" : \"SESS_NOT_EXISTS\"}")); - ws.write(buffer.data()); - } + // add process ids + sessCur->addProcId(GetProcessId(appProcInfo.hProcess)); + //sessCur->addProcId(GetProcessId(nodeProcInfo.hProcess)); } + LOG((std::stringstream() << "session [" + << sessId << "] created, (httpPort: " << sessHttpPort << "), (streamPort: " << std::to_string(sessStreamPort) + ")"), 10); - - // Echo the message back - // ws.text(ws.got_text()); - // boost::beast::ostream(buffer) << "something"; - // ws.write(buffer.data()); - - } - catch (beast::system_error const& se) + // send respond + buffer.clear(); + boost::beast::ostream(buffer) + << (std::string("{\"message\" : \"SESS_CREATION\", \"id\" : \"") + + std::to_string(sessId) + "\", \"port\" : \"" + std::to_string(sessHttpPort) + "\"}"); + ws.write(buffer.data()); + } + else if (message == "SESS_CONNECT") + { + // read session id + try { - LOG((std::stringstream() << "user(" << thisUserNum << ") disconnected"), 7); - - if (sessId) + jsonData = nlohmann::json::parse(beast::buffers_to_string(buffer.data())); + content = jsonData.at("content"); + } + catch (nlohmann::json::parse_error& e) + { + std::cout << "Parse error:" << e.what() << std::endl; + } + catch (nlohmann::json::out_of_range& e) + { + std::cout << "Out of range:" << e.what() << std::endl; + } + catch (nlohmann::json::exception& e) + { + std::cout << "JSON exception: " << e.what() << std::endl; + } + catch (std::exception& e) + { + std::cout << "Unknown exception" << e.what() << std::endl; + } + + sessId = atoi(content.c_str()); + // connect to session + if (sessManager.getById(sessId, &sessCur)) + { + if (sessCur->existsUserId(user_id)) { - sessManager.getById(sessId, &sessCur); - if (sessCur == nullptr) - { - std::cout << "nullptr session" << std::endl; - continue; - } - - sessCur->removeUserById(user_id); - - if (!sessCur->getUsersCount()) - { - LOG((std::stringstream() << "session [" << sessCur->getId() << "] destroyed"), 12); - std::vector procIds(sessCur->getProcIds()); - for (auto& id : procIds) - EndProc(id); - sessManager.remove(sessId); - } + LOG((std::stringstream() << "user exists: " << user_id), 7); + continue; } - break; + sessCur->addUser(user(user_id)); + LOG((std::stringstream() << "user(" << thisUserNum << ") connected to session (id: " << sessCur->getId() + << "), (users: " << sessCur->getUsersCount() << ")"), 14); - - - /* if (se.code() != websocket::error::closed) - { - std::cerr << "Error: " << se.code().message() << std::endl; - break; - }*/ + buffer.clear(); + boost::beast::ostream(buffer) + << (std::string("{\"message\" : \"SESS_CONNECT\", \"content\" : \"") + std::to_string(sessCur->getPort()) + "\"}"); + ws.write(buffer.data()); + } + else + { + LOG((std::stringstream() << "no session (id: " << content << ")"), 14); + buffer.clear(); + boost::beast::ostream(buffer) + << (std::string("{\"message\" : \"SESS_NOT_EXISTS\"}")); + ws.write(buffer.data()); } } - }).detach(); + + + + // Echo the message back + // ws.text(ws.got_text()); + // boost::beast::ostream(buffer) << "something"; + // ws.write(buffer.data()); + } + catch (const std::exception& e) + { + LOG((std::stringstream() << "Error: " << e.what()), 14); + if (sessId) + { + sessManager.getById(sessId, &sessCur); + if (sessCur == nullptr) + { + std::cout << "nullptr session" << std::endl; + } + else + { + sessCur->removeUserById(user_id); + + if (!sessCur->getUsersCount()) + { + LOG((std::stringstream() << "session [" << sessCur->getId() << "] destroyed"), 12); + std::vector procIds(sessCur->getProcIds()); + for (auto& id : procIds) + EndProc(id); + sessManager.remove(sessId); + } + } + } + LOG((std::stringstream() << "user(" << thisUserNum << ") disconnected"), 7); + break; + } + catch (beast::system_error const& se) + { + LOG((std::stringstream() << "user(" << thisUserNum << ") fdisconnected"), 7); + break; + + /* if (se.code() != websocket::error::closed) + { + std::cerr << "Error: " << se.code().message() << std::endl; + break; + }*/ + } } - logFile.close(); - return 0; } \ No newline at end of file diff --git a/ServerPixel/ServerPixel.vcxproj b/ServerPixel/ServerPixel.vcxproj index 6a9e581..7429a12 100644 --- a/ServerPixel/ServerPixel.vcxproj +++ b/ServerPixel/ServerPixel.vcxproj @@ -148,12 +148,14 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - C:\Libraries\json-develop\include;C:\Libraries\boost\include\boost_1_79_0;%(AdditionalIncludeDirectories) + C:\Libraries\openssl\include;C:\Libraries\json-develop\include;C:\Libraries\boost\include\boost_1_79_0;%(AdditionalIncludeDirectories) stdcpp17 Console true + C:\Libraries\openssl\apps\lib;%(AdditionalLibraryDirectories) + C:\Libraries\openssl\libcrypto.lib;C:\Libraries\openssl\libssl.lib;%(AdditionalDependencies) @@ -164,7 +166,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - C:\Libraries\json-develop\include;C:\Libraries\boost\include\boost_1_79_0;%(AdditionalIncludeDirectories) + C:\Libraries\json-develop\include;C:\Libraries\openssl\include;C:\Libraries\boost\include\boost_1_79_0;%(AdditionalIncludeDirectories) stdcpp17 @@ -172,6 +174,7 @@ true true true + C:\Libraries\openssl\libcrypto.lib;C:\Libraries\openssl\libssl.lib;%(AdditionalDependencies) diff --git a/ServerPixel/sessionManager.h b/ServerPixel/sessionManager.h index 021d86a..ff024d8 100644 --- a/ServerPixel/sessionManager.h +++ b/ServerPixel/sessionManager.h @@ -2,6 +2,7 @@ #include #include +#include #pragma once class sessionManager { @@ -58,16 +59,31 @@ public: return limit == sessions.size(); } - uint16_t getMinPort() + uint16_t getMinPort(uint16_t desired) { + + // working not properly if (!sessions.size()) - return 0; + return desired; std::vector ports; for (auto& s : sessions) ports.push_back(s.getPort()); - std::sort(ports.begin(), ports.end(), [](uint16_t a, uint16_t b) { return a > b; }); - return *(ports.begin()); + std::cout << "amount: " << ports.size() << std::endl; + std::cout << "ports: " << std::endl; + for (auto& s : ports) + std::cout << s << ", "; + std::cout << std::endl; + + std::sort(ports.begin(), ports.end(), [](uint16_t a, uint16_t b) { return a < b; }); + uint16_t port = desired; + for (auto p : ports) + { + if (desired != p) + return desired; + ++desired; + } + return ports.back() + 1; } };