#ifndef TLS_NETWORK_HPP_ #define TLS_NETWORK_HPP_ #include #include #include #include #include #include #include #include #include using std::cout; using std::cerr; using std::endl; class NetworkManager { private: WebSocket& web; SSL_CTX* ctx; int sock = -1; std::unique_ptr ssl = { nullptr, &SSL_free }; void handleSSLInitErrors() { ERR_print_errors_fp(stderr); abort(); } void createSSLConnection(const std::string& hostname, const std::string& port) { addrinfo hints = { 0 }, *res = { 0 }; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res) != 0) { cerr << "Failed to get address info for " << hostname << ":" << port << endl; handleSSLInitErrors(); } sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock == -1) { cerr << "Failed to create socket" << endl; handleSSLInitErrors(); } if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) { cerr << "Failed to connect to " << hostname << ":" << port << endl; close(sock); handleSSLInitErrors(); } ssl.reset(SSL_new(ctx)); if (!ssl) { cerr << "Failed to create SSL structure" << endl; close(sock); handleSSLInitErrors(); } SSL_set_fd(ssl.get(), sock); if (SSL_connect(ssl.get()) != 1) { cerr << "SSL connection failed" << endl; close(sock); handleSSLInitErrors(); } freeaddrinfo(res); } NetworkManager& operator=(const NetworkManager&) = delete; NetworkManager(const NetworkManager&) = delete; NetworkManager() : ctx(nullptr), web(WebSocket::getInstance()) { if (!ctx) { ctx = SSL_CTX_new(TLS_client_method()); if (!ctx) { cerr << "Failed to create SSL context" << endl; handleSSLInitErrors(); } OPENSSL_init_ssl(0, 0); OPENSSL_add_all_algorithms_noconf(); SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); createSSLConnection("discord.com", "443"); } } std::string parseJson(const std::string& response) { unsigned long jsonStart = response.find("\r\n\r\n"); if (jsonStart == std::string::npos) { return ""; } jsonStart += 4; std::string jsonString = response.substr(jsonStart); unsigned long jsonBegin = jsonString.find('['), jsonEnd = jsonString.rfind(']'); if (jsonBegin == std::string::npos || jsonEnd == std::string::npos || jsonEnd < jsonBegin) { return ""; } jsonString = jsonString.substr(jsonBegin, jsonEnd - jsonBegin + 1); jsonString.erase(std::remove(jsonString.begin(), jsonString.end(), '\r'), jsonString.end()); jsonString.erase(std::remove(jsonString.begin(), jsonString.end(), '\t'), jsonString.end()); jsonString.erase(std::remove(jsonString.begin(), jsonString.end(), '\n'), jsonString.end()); jsonString.erase(std::remove(jsonString.begin(), jsonString.end(), '\\'), jsonString.end()); jsonString.erase(std::remove(jsonString.begin(), jsonString.end(), '\''), jsonString.end()); jsonString.erase(std::remove(jsonString.begin(), jsonString.end(), ' '), jsonString.end()); try { nlohmann::json jsonData = nlohmann::json::parse(jsonString); return jsonData.dump(); } catch (const nlohmann::json::parse_error& e) { return ""; } } public: static NetworkManager& getInstance() { static NetworkManager instance; return instance; } ~NetworkManager() { if (ctx) SSL_CTX_free(ctx); close(sock); } std::string request(const std::string& method, const std::string& path, const std::string& data = "") { #ifdef DEBUG createSSLConnection("discord.com", "443"); #endif std::string result; auto net = [this, method, path, data, &result]() mutable { std::string request = method + " " + path + " HTTP/1.1\r\nHost: discord.com\r\nAccept: application/json\r\nContent-Type: application/json\r\n"; request += "Authorization: " + web.getToken() + "\r\nContent-Length: " + std::to_string(data.length()) + "\r\n"; #ifdef DEBUG request += "Connection: close\r\n\r\n"; #elif defined(RELEASE) request += "Connection: keep-alive\r\n\r\n"; #endif request += data; if (SSL_write(ssl.get(), request.c_str(), request.length()) <= 0) { std::cerr << "Failed to send request" << std::endl; handleSSLInitErrors(); } #ifdef DEBUG std::vector buffer(1024); int bytesRead; while ((bytesRead = SSL_read(ssl.get(), buffer.data(), buffer.size())) > 0) { result.append(buffer.data(), bytesRead); if (bytesRead == buffer.size()) { buffer.resize(buffer.size() * 2); } } #endif }; net(); #ifdef DEBUG return parseJson(result); #elif defined(RELEASE) return result; #endif } }; #endif