134 lines
5.2 KiB
C++
134 lines
5.2 KiB
C++
#ifndef TLS_NETWORK_HPP_
|
|
#define TLS_NETWORK_HPP_
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#include <includes.h>
|
|
#include <net/if.h>
|
|
#include <netdb.h>
|
|
#include <utils/types.hpp>
|
|
#include <gateway/Websocket.hpp>
|
|
using std::string, std::cout, std::endl, nlohmann::json;
|
|
class NetworkManager {
|
|
private:
|
|
WebSocket& web;
|
|
SSL_CTX* ctx;
|
|
int sock = -1;
|
|
std::unique_ptr<SSL, decltype(&SSL_free)> ssl = { nullptr, &SSL_free };
|
|
void handleSSLInitErrors() {
|
|
ERR_print_errors_fp(stderr);
|
|
abort();
|
|
}
|
|
void createSSLConnection(const std::string& hostname, const std::string& port) {
|
|
Logs::create(INFO, NETWORK, "SSL connection");
|
|
addrinfo hints = { 0 }, *res = { 0 };
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
if (getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res) != 0) {
|
|
Logs::create(ERROR, NETWORK, "Failed to get address info for " + hostname + ":" + port);
|
|
handleSSLInitErrors();
|
|
}
|
|
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (sock == -1) {
|
|
Logs::create(ERROR, NETWORK, "Socket error");
|
|
close(sock);
|
|
handleSSLInitErrors();
|
|
}
|
|
if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
|
|
Logs::create(ERROR, NETWORK, "Failed to connect to " + hostname + ":" + port);
|
|
close(sock);
|
|
handleSSLInitErrors();
|
|
}
|
|
ssl.reset(SSL_new(ctx));
|
|
if (!ssl) {
|
|
Logs::create(ERROR, NETWORK, "Failed to create SSL structure");
|
|
close(sock);
|
|
handleSSLInitErrors();
|
|
}
|
|
SSL_set_fd(ssl.get(), sock);
|
|
if (SSL_connect(ssl.get()) != 1) {
|
|
Logs::create(ERROR, NETWORK, "SSL connection failed");
|
|
close(sock);
|
|
handleSSLInitErrors();
|
|
}
|
|
freeaddrinfo(res);
|
|
}
|
|
NetworkManager& operator=(const NetworkManager&) = delete;
|
|
NetworkManager(const NetworkManager&) = delete;
|
|
NetworkManager() : web(WebSocket::getInstance()) {
|
|
Logs::create(INFO, NETWORK, "Network init");
|
|
if (!ctx) {
|
|
ctx = SSL_CTX_new(TLS_client_method());
|
|
if (!ctx) {
|
|
Logs::create(ERROR, NETWORK, "Failed to create SSL context");
|
|
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 extract(const std::string& httpResponse) {
|
|
unsigned long pos = httpResponse.find("\r\n\r\n");
|
|
if (pos != std::string::npos) {
|
|
std::string json, chunkSizeStr;
|
|
std::istringstream stream(httpResponse.substr(pos + 4));
|
|
while (std::getline(stream, chunkSizeStr)) {
|
|
unsigned long chunkSize = std::stoul(chunkSizeStr, nullptr, 16);
|
|
if (chunkSize == 0) break;
|
|
std::string chunk(chunkSize, '\0');
|
|
stream.read(&chunk[0], chunkSize);
|
|
json.append(chunk);
|
|
stream.ignore(2);
|
|
}
|
|
return json;
|
|
}
|
|
return "";
|
|
}
|
|
public:
|
|
static NetworkManager& getInstance() {
|
|
Logs::create(WARNING, NETWORK, "Instance event");
|
|
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 = "", bool closeConnection = true) {
|
|
if (closeConnection) createSSLConnection("discord.com", "443");
|
|
auto net = [this, method, path, data, closeConnection, result = std::string("")]() mutable -> std::string {
|
|
std::string request = method + " " + path + " HTTP/1.1\r\n";
|
|
request += "Host: discord.com\r\n";
|
|
request += "Accept: application/json\r\n";
|
|
request += "Content-Type: application/json\r\n";
|
|
request += "Authorization: " + web.getToken() + "\r\n";
|
|
request += "Content-Length: " + std::to_string(data.length()) + "\r\n";
|
|
if (closeConnection) {
|
|
request += "Connection: close\r\n\r\n";
|
|
} else {
|
|
request += "Connection: keep-alive\r\n\r\n";
|
|
}
|
|
request += data;
|
|
if (SSL_write(ssl.get(), request.c_str(), request.length()) <= 0) {
|
|
Logs::create(ERROR, NETWORK, "Failed to send request");
|
|
handleSSLInitErrors();
|
|
return "";
|
|
}
|
|
Logs::create(INFO, NETWORK, "Request " + method + " " + path);
|
|
if (closeConnection) {
|
|
std::vector<char> 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);
|
|
}
|
|
}
|
|
}
|
|
return extract(result);
|
|
};
|
|
return net();
|
|
}
|
|
};
|
|
#endif |