larra/tests/proxy.cpp

199 lines
6.2 KiB
C++
Raw Normal View History

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};
2024-11-18 21:07:30 +00:00
std::string targetHost = "target_host";
uint16_t targetPort = 80;
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
std::string expectedRequest =
std::format("CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n\r\n", targetHost, targetPort, targetHost, targetPort);
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
std::string proxyResponse = "HTTP/1.1 200 Connection established\r\n\r\n";
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
mock_socket.AddReceivedData(proxyResponse);
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
bool connectSuccessful = false;
2024-11-11 22:55:26 +00:00
asio::co_spawn(
io_context,
[&]() -> asio::awaitable<void> {
try {
2024-11-18 21:07:30 +00:00
co_await client::impl::ConnectViaProxy(mock_socket, proxy, targetHost, targetPort);
connectSuccessful = true;
2024-11-11 22:55:26 +00:00
} catch(...) {
2024-11-18 21:07:30 +00:00
connectSuccessful = false;
2024-11-11 22:55:26 +00:00
}
},
asio::detached);
io_context.run();
2024-11-18 21:07:30 +00:00
std::string sentData = mock_socket.GetSentData();
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
EXPECT_EQ(sentData, expectedRequest);
EXPECT_TRUE(connectSuccessful);
2024-11-11 22:55:26 +00:00
}
// 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
2024-11-18 21:07:30 +00:00
std::string serverResponse;
serverResponse += "\x05\x00"; // VER, METHOD
serverResponse += "\x05\x00\x00\x01"; // VER, REP, RSV, ATYP (IPv4)
serverResponse += "\x7F\x00\x00\x01"; // BND.ADDR (127.0.0.1)
serverResponse += "\x1F\x90"; // BND.PORT (8080)
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
socket.AddReceivedData(serverResponse);
2024-11-11 22:55:26 +00:00
Socks5Proxy proxy{"proxy.example.com", 1080};
2024-11-18 21:07:30 +00:00
std::string targetHostname = "target.example.com";
std::uint16_t targetPort = 80;
2024-11-11 22:55:26 +00:00
boost::asio::co_spawn(
executor,
[&]() -> boost::asio::awaitable<void> {
2024-11-18 21:07:30 +00:00
co_await client::impl::ConnectViaProxy(socket, proxy, targetHostname, targetPort);
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
auto sentData = socket.GetSentData();
2024-11-11 22:55:26 +00:00
// Expected client greeting
2024-11-18 21:07:30 +00:00
std::string expectedGreeting = "\x05\x01\x00";
2024-11-11 22:55:26 +00:00
// Expected CONNECT request
2024-11-18 21:07:30 +00:00
std::array<std::uint8_t, 262> expectedRequest{};
2024-11-11 22:55:26 +00:00
std::size_t req_len = 0;
2024-11-18 21:07:30 +00:00
expectedRequest[req_len++] = 0x05; // VER
expectedRequest[req_len++] = 0x01; // CMD: CONNECT
expectedRequest[req_len++] = 0x00; // RSV
expectedRequest[req_len++] = 0x03; // ATYP: DOMAINNAME
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
expectedRequest[req_len++] = static_cast<std::uint8_t>(targetHostname.size()); // domain length
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
std::memcpy(&expectedRequest[req_len], targetHostname.data(), targetHostname.size());
req_len += targetHostname.size();
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
std::uint16_t networkOrderPort = htons(targetPort);
expectedRequest[req_len++] = static_cast<std::uint8_t>((networkOrderPort >> 8) & 0xFF);
expectedRequest[req_len++] = static_cast<std::uint8_t>(networkOrderPort & 0xFF);
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
std::string expectedData = expectedGreeting;
auto transformed_view = expectedRequest | std::views::take(req_len) | std::views::transform([](std::uint8_t byte) {
2024-11-12 23:03:30 +00:00
return static_cast<char>(byte);
});
2024-11-11 22:55:26 +00:00
2024-11-18 21:07:30 +00:00
expectedData.append(std::ranges::to<std::string>(transformed_view));
EXPECT_EQ(sentData, expectedData);
2024-11-11 22:55:26 +00:00
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());
}
*/