2024-11-11 22:55:26 +00:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
|
|
#include <larra/client/client.hpp>
|
|
|
|
#include <larra/impl/mock_socket.hpp>
|
|
|
|
#include <larra/proxy.hpp>
|
|
|
|
|
|
|
|
using namespace larra::xmpp;
|
|
|
|
using boost::asio::ip::tcp;
|
|
|
|
namespace asio = boost::asio;
|
|
|
|
|
|
|
|
class ProxyTest : public ::testing::Test {
|
|
|
|
protected:
|
|
|
|
boost::asio::io_context io_context;
|
|
|
|
larra::xmpp::impl::MockSocket mock_socket{io_context.get_executor()};
|
|
|
|
};
|
|
|
|
|
|
|
|
// Test 1: Connect via HTTP proxy with successful server response
|
|
|
|
TEST_F(ProxyTest, ConnectViaHttpProxy_SuccessfulResponse) {
|
|
|
|
HttpProxy proxy{"proxy_host", 8080};
|
|
|
|
|
|
|
|
std::string target_host = "target_host";
|
|
|
|
uint16_t target_port = 80;
|
|
|
|
|
|
|
|
std::string expected_request =
|
|
|
|
std::format("CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n", target_host, target_port, target_host, target_port);
|
|
|
|
|
|
|
|
std::string proxy_response = "HTTP/1.1 200 Connection established\r\n\r\n";
|
|
|
|
|
|
|
|
mock_socket.AddReceivedData(proxy_response);
|
|
|
|
|
|
|
|
bool connect_successful = false;
|
|
|
|
|
|
|
|
asio::co_spawn(
|
|
|
|
io_context,
|
|
|
|
[&]() -> asio::awaitable<void> {
|
|
|
|
try {
|
|
|
|
co_await client::impl::ConnectViaProxy(mock_socket, proxy, target_host, target_port);
|
|
|
|
connect_successful = true;
|
|
|
|
} catch(...) {
|
|
|
|
connect_successful = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
asio::detached);
|
|
|
|
|
|
|
|
io_context.run();
|
|
|
|
|
|
|
|
std::string sent_data = mock_socket.GetSentData();
|
|
|
|
|
|
|
|
EXPECT_EQ(sent_data, expected_request);
|
|
|
|
EXPECT_TRUE(connect_successful);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test 2: Connect via SOCKS proxy
|
|
|
|
TEST(Socks5ProxyTest, ConnectViaProxy) {
|
|
|
|
boost::asio::io_context io;
|
|
|
|
auto executor = io.get_executor();
|
|
|
|
|
|
|
|
larra::xmpp::impl::MockSocket socket{executor};
|
|
|
|
|
|
|
|
// expected server responses
|
|
|
|
std::string server_response;
|
|
|
|
server_response += "\x05\x00"; // VER, METHOD
|
|
|
|
server_response += "\x05\x00\x00\x01"; // VER, REP, RSV, ATYP (IPv4)
|
|
|
|
server_response += "\x7F\x00\x00\x01"; // BND.ADDR (127.0.0.1)
|
|
|
|
server_response += "\x1F\x90"; // BND.PORT (8080)
|
|
|
|
|
|
|
|
socket.AddReceivedData(server_response);
|
|
|
|
|
|
|
|
Socks5Proxy proxy{"proxy.example.com", 1080};
|
|
|
|
std::string target_hostname = "target.example.com";
|
|
|
|
std::uint16_t target_port = 80;
|
|
|
|
|
|
|
|
boost::asio::co_spawn(
|
|
|
|
executor,
|
|
|
|
[&]() -> boost::asio::awaitable<void> {
|
|
|
|
co_await client::impl::ConnectViaProxy(socket, proxy, target_hostname, target_port);
|
|
|
|
|
|
|
|
auto sent_data = socket.GetSentData();
|
|
|
|
|
|
|
|
// Expected client greeting
|
|
|
|
std::string expected_greeting = "\x05\x01\x00";
|
|
|
|
|
|
|
|
// Expected CONNECT request
|
|
|
|
std::array<std::uint8_t, 262> expected_request{};
|
|
|
|
std::size_t req_len = 0;
|
|
|
|
|
|
|
|
expected_request[req_len++] = 0x05; // VER
|
|
|
|
expected_request[req_len++] = 0x01; // CMD: CONNECT
|
|
|
|
expected_request[req_len++] = 0x00; // RSV
|
|
|
|
expected_request[req_len++] = 0x03; // ATYP: DOMAINNAME
|
|
|
|
|
|
|
|
expected_request[req_len++] = static_cast<std::uint8_t>(target_hostname.size()); // domain length
|
|
|
|
|
|
|
|
std::memcpy(&expected_request[req_len], target_hostname.data(), target_hostname.size());
|
|
|
|
req_len += target_hostname.size();
|
|
|
|
|
|
|
|
std::uint16_t network_order_port = htons(target_port);
|
|
|
|
expected_request[req_len++] = static_cast<std::uint8_t>((network_order_port >> 8) & 0xFF);
|
|
|
|
expected_request[req_len++] = static_cast<std::uint8_t>(network_order_port & 0xFF);
|
|
|
|
|
|
|
|
std::string expected_data = expected_greeting;
|
2024-11-12 23:03:30 +00:00
|
|
|
auto transformed_view = expected_request | std::views::take(req_len) | std::views::transform([](std::uint8_t byte) {
|
|
|
|
return static_cast<char>(byte);
|
|
|
|
});
|
2024-11-11 22:55:26 +00:00
|
|
|
|
2024-11-12 23:03:30 +00:00
|
|
|
expected_data.append(std::ranges::to<std::string>(transformed_view));
|
2024-11-11 22:55:26 +00:00
|
|
|
EXPECT_EQ(sent_data, expected_data);
|
|
|
|
|
|
|
|
co_return;
|
|
|
|
},
|
|
|
|
boost::asio::detached);
|
|
|
|
|
|
|
|
io.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
// Test 3: Connect via system configured proxy
|
|
|
|
TEST_F(ProxyTest, ConnectViaSystemProxy_HttpProxy) {
|
|
|
|
// Set the environment variable for the system proxy
|
|
|
|
const char* original_http_proxy = std::getenv("http_proxy");
|
|
|
|
setenv("http_proxy", "http://proxy_host:8080", 1);
|
|
|
|
|
|
|
|
std::string target_host = "target_host";
|
|
|
|
uint16_t target_port = 80;
|
|
|
|
|
|
|
|
std::string expected_request = std::format(
|
|
|
|
"CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n",
|
|
|
|
target_host, target_port, target_host, target_port);
|
|
|
|
|
|
|
|
std::string proxy_response = "HTTP/1.1 200 Connection established\r\n\r\n";
|
|
|
|
|
|
|
|
mock_socket.AddReceivedData(proxy_response);
|
|
|
|
|
|
|
|
bool connect_successful = false;
|
|
|
|
|
|
|
|
asio::co_spawn(io_context, [&]() -> asio::awaitable<void> {
|
|
|
|
try {
|
|
|
|
SystemConfiguredProxy proxy;
|
|
|
|
co_await client::impl::ConnectToServer(mock_socket, proxy, target_host, target_port);
|
|
|
|
connect_successful = true;
|
|
|
|
} catch (...) {
|
|
|
|
connect_successful = false;
|
|
|
|
}
|
|
|
|
}, asio::detached);
|
|
|
|
|
|
|
|
io_context.run();
|
|
|
|
|
|
|
|
std::string sent_data = mock_socket.GetSentData();
|
|
|
|
|
|
|
|
EXPECT_EQ(sent_data, expected_request);
|
|
|
|
EXPECT_TRUE(connect_successful);
|
|
|
|
|
|
|
|
// Restore the original environment variable
|
|
|
|
if (original_http_proxy) {
|
|
|
|
setenv("http_proxy", original_http_proxy, 1);
|
|
|
|
} else {
|
|
|
|
unsetenv("http_proxy");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test 4: Incorrect proxy data
|
|
|
|
TEST_F(ProxyTest, ConnectViaHttpProxy_IncorrectData) {
|
|
|
|
HttpProxy proxy{"proxy_host", 8080};
|
|
|
|
|
|
|
|
std::string target_host = "target_host";
|
|
|
|
uint16_t target_port = 80;
|
|
|
|
|
|
|
|
std::string expected_request = std::format(
|
|
|
|
"CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n",
|
|
|
|
target_host, target_port, target_host, target_port);
|
|
|
|
|
|
|
|
// Simulate an incorrect response from the proxy server
|
|
|
|
std::string proxy_response = "HTTP/1.1 400 Bad Request\r\n\r\n";
|
|
|
|
|
|
|
|
mock_socket.AddReceivedData(proxy_response);
|
|
|
|
|
|
|
|
bool connect_successful = false;
|
|
|
|
std::string error_message;
|
|
|
|
|
|
|
|
asio::co_spawn(io_context, [&]() -> asio::awaitable<void> {
|
|
|
|
try {
|
|
|
|
co_await client::impl::ConnectToServer(mock_socket, proxy, target_host, target_port);
|
|
|
|
connect_successful = true;
|
|
|
|
} catch (const std::runtime_error& e) {
|
|
|
|
connect_successful = false;
|
|
|
|
error_message = e.what();
|
|
|
|
}
|
|
|
|
}, asio::detached);
|
|
|
|
|
|
|
|
io_context.run();
|
|
|
|
|
|
|
|
std::string sent_data = mock_socket.GetSentData();
|
|
|
|
|
|
|
|
EXPECT_EQ(sent_data, expected_request);
|
|
|
|
EXPECT_FALSE(connect_successful);
|
|
|
|
EXPECT_FALSE(error_message.empty());
|
|
|
|
}
|
|
|
|
*/
|