-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/socks_transport' into 'master'
tcp_transport: Adds SOCKS4 proxy transport See merge request espressif/esp-idf!20479
- Loading branch information
Showing
23 changed files
with
718 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
cmake_minimum_required(VERSION 3.16) | ||
|
||
set(COMPONENTS esp_timer lwip tcp_transport main) | ||
|
||
list(APPEND EXTRA_COMPONENT_DIRS | ||
"$ENV{IDF_PATH}/tools/mocks/lwip/" | ||
"$ENV{IDF_PATH}/tools/mocks/freertos/" | ||
"$ENV{IDF_PATH}/tools/mocks/esp_timer/" | ||
"$ENV{IDF_PATH}/tools/mocks/esp-tls/" | ||
) | ||
|
||
include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||
|
||
project(host_tcp_transport_test) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
| Supported Targets | Linux | | ||
| ----------------- | ----- | | ||
|
||
# Description | ||
|
||
This directory contains test code for `tcp_transport` that runs on host. | ||
|
||
Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework | ||
|
||
# Build | ||
|
||
Tests build regularly like an idf project. | ||
|
||
``` | ||
idf.py build | ||
``` | ||
|
||
# Run | ||
|
||
The build produces an executable in the build folder. | ||
|
||
Just run: | ||
|
||
``` | ||
./build/host_tcp_transport_test.elf | ||
``` | ||
|
||
The test executable have some options provided by the test framework. | ||
|
6 changes: 6 additions & 0 deletions
6
components/tcp_transport/host_test/components/mocked_transport/CMakeLists.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# NOTE: This kind of mocking currently works on Linux targets only. | ||
# On Espressif chips, too many dependencies are missing at the moment. | ||
|
||
idf_component_mock(INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} | ||
REQUIRES tcp_transport | ||
MOCK_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/mock_transport.h) |
9 changes: 9 additions & 0 deletions
9
components/tcp_transport/host_test/components/mocked_transport/mock/mock_config.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
:cmock: | ||
:plugins: | ||
- expect | ||
- expect_any_args | ||
- return_thru_ptr | ||
- array | ||
- ignore | ||
- ignore_arg | ||
- callback |
22 changes: 22 additions & 0 deletions
22
components/tcp_transport/host_test/components/mocked_transport/mock_transport.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#pragma once | ||
|
||
#include "esp_transport.h" | ||
|
||
int mock_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms); | ||
|
||
int mock_close(esp_transport_handle_t t); | ||
|
||
int mock_write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms); | ||
|
||
int mock_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms); | ||
|
||
int mock_poll_read(esp_transport_handle_t t, int timeout_ms); | ||
|
||
int mock_poll_write(esp_transport_handle_t t, int timeout_ms); | ||
|
||
esp_err_t mock_destroy(esp_transport_handle_t t); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
idf_component_register(SRCS "test_socks_transport.cpp" "catch_main.cpp" | ||
REQUIRES tcp_transport mocked_transport | ||
INCLUDE_DIRS "$ENV{IDF_PATH}/tools" | ||
WHOLE_ARCHIVE) | ||
|
||
idf_component_get_property(lwip_component lwip COMPONENT_LIB) | ||
idf_component_get_property(esp_timer_component esp_timer COMPONENT_LIB) | ||
idf_component_get_property(tcp_transport_component tcp_transport COMPONENT_LIB) | ||
target_link_libraries(${tcp_transport_component} PUBLIC ${lwip_component} ${esp_timer_component}) | ||
target_compile_options(${COMPONENT_LIB} PUBLIC -fsanitize=address -fconcepts) | ||
target_link_options(${COMPONENT_LIB} PUBLIC -fsanitize=address) | ||
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 20) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#define CATCH_CONFIG_MAIN | ||
#include "catch/catch.hpp" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
dependencies: | ||
espressif/fmt: "^9.1.0" | ||
idf: | ||
version: ">=4.1.0" |
185 changes: 185 additions & 0 deletions
185
components/tcp_transport/host_test/main/test_socks_transport.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#include <cstdint> | ||
#include <cstdlib> | ||
#include <cstddef> | ||
#include <memory> | ||
#include <string> | ||
#include <type_traits> | ||
#include <array> | ||
#include <vector> | ||
#include <netinet/in.h> | ||
#include <sys/socket.h> | ||
#include <netdb.h> | ||
#include "fmt/core.h" | ||
#include "fmt/ranges.h" | ||
#include "catch/catch.hpp" | ||
#include "esp_transport.h" | ||
#include "esp_transport_socks_proxy.h" | ||
|
||
extern "C" { | ||
#include "Mockmock_transport.h" | ||
#include "Mocknetdb.h" | ||
#include "Mockesp_timer.h" | ||
|
||
uint16_t lwip_htons(uint16_t n) | ||
{ | ||
return __builtin_bswap16(n); | ||
} | ||
} | ||
|
||
using unique_transport = std::unique_ptr<std::remove_pointer_t<esp_transport_handle_t>, decltype(&esp_transport_destroy)>; | ||
using namespace std::literals; | ||
|
||
namespace { | ||
|
||
/* | ||
* Makes possible to pass a capturing lambda as a callback | ||
*/ | ||
decltype(auto) capture_lambda(auto callable) | ||
{ | ||
// make a static copy of the lambda to extend it's lifetime and avoid the capture. | ||
[[maybe_unused]]static auto call = callable; | ||
return []<typename... Args>(Args... args) { | ||
return call(args...); | ||
}; | ||
} | ||
|
||
auto make_response(socks_transport_error_t response) | ||
{ | ||
return std::array<char, 8>({0x00, static_cast<char>(response), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); | ||
} | ||
} | ||
|
||
TEST_CASE("Initial", "[Initialization]") | ||
{ | ||
esp_transport_socks_proxy_config_t config{ .version = SOCKS4, | ||
.address = "test_socks4_proxy", | ||
.port = 1080}; | ||
|
||
mock_destroy_IgnoreAndReturn(ESP_OK); | ||
unique_transport test_parent{esp_transport_init(), esp_transport_destroy}; | ||
esp_transport_set_func(test_parent.get(), mock_connect, mock_read, mock_write, mock_close, mock_poll_read, mock_poll_write, mock_destroy); | ||
|
||
SECTION("Initialize with invalid parent transport") { | ||
esp_transport_handle_t parent_handle = nullptr; | ||
unique_transport socks_transport{esp_transport_socks_proxy_init(parent_handle, &config), esp_transport_destroy}; | ||
REQUIRE(socks_transport == nullptr); | ||
} | ||
|
||
SECTION("Initialize with NULL config") { | ||
auto *socks_transport = esp_transport_socks_proxy_init(test_parent.get(), nullptr); | ||
REQUIRE(socks_transport == nullptr); | ||
} | ||
|
||
SECTION("Initialize with NULL address config") { | ||
config.address = nullptr; | ||
auto *socks_transport = esp_transport_socks_proxy_init(test_parent.get(), &config); | ||
REQUIRE(socks_transport == nullptr); | ||
} | ||
|
||
SECTION("Successful Initialization") { | ||
auto *socks_transport = esp_transport_socks_proxy_init(test_parent.get(), &config); | ||
REQUIRE(socks_transport != nullptr); | ||
esp_transport_destroy(socks_transport); | ||
} | ||
} | ||
|
||
TEST_CASE("Requests to Proxy", "[Requests]") | ||
{ | ||
constexpr auto timeout = 50; | ||
esp_transport_socks_proxy_config_t config{ .version = SOCKS4, | ||
.address = "test_socks4_proxy", | ||
.port = 1080}; | ||
|
||
auto test_target = "test_target"sv; | ||
auto target_port = 80; | ||
unique_transport test_parent{esp_transport_init(), esp_transport_destroy}; | ||
REQUIRE(test_parent); | ||
esp_transport_set_func(test_parent.get(), mock_connect, mock_read, mock_write, mock_close, mock_poll_read, mock_poll_write, mock_destroy); | ||
unique_transport socks_transport{esp_transport_socks_proxy_init(test_parent.get(), &config), esp_transport_destroy}; | ||
|
||
mock_destroy_IgnoreAndReturn(ESP_OK); | ||
esp_timer_get_time_IgnoreAndReturn(0); | ||
|
||
SECTION("Failure to connect to proxy") { | ||
mock_connect_ExpectAndReturn(test_parent.get(), config.address, config.port, timeout, -1); | ||
REQUIRE(esp_transport_connect(socks_transport.get(), test_target.data(), target_port, timeout) == -1); | ||
} | ||
|
||
GIVEN("Proxy accepted the connection") { | ||
|
||
mock_connect_ExpectAndReturn(test_parent.get(), config.address, config.port, timeout, 0); | ||
auto expect_addr_info = [](std::string_view test_target, int return_value) { | ||
lwip_getaddrinfo_ExpectAndReturn(test_target.data(), nullptr, nullptr, nullptr, return_value); | ||
lwip_getaddrinfo_IgnoreArg_hints(); | ||
lwip_getaddrinfo_IgnoreArg_res(); | ||
struct addrinfo addr_info = {}; | ||
struct sockaddr_in sockaddr = {}; | ||
sockaddr.sin_addr.s_addr = 0x5a5a5a5a; | ||
lwip_freeaddrinfo_Ignore(); | ||
return std::tuple{addr_info, sockaddr}; | ||
}; | ||
|
||
SECTION("Failure to resolve target") { | ||
auto [addr_info, sockaddr] = expect_addr_info(test_target, EAI_NONAME); | ||
addr_info.ai_addr = reinterpret_cast<struct sockaddr *>(&sockaddr); | ||
auto *p_addr_info = &addr_info; | ||
lwip_getaddrinfo_ReturnThruPtr_res(&p_addr_info); | ||
REQUIRE(esp_transport_connect(socks_transport.get(), "test_target", 8080, timeout) == -1); | ||
REQUIRE(errno == SOCKS_RESPONSE_TARGET_NOT_FOUND); | ||
} | ||
|
||
GIVEN("Success on target resolution") { | ||
auto [addr_info, sockaddr] = expect_addr_info(test_target, 0); | ||
addr_info.ai_addr = reinterpret_cast<struct sockaddr *>(&sockaddr); | ||
auto *p_addr_info = &addr_info; | ||
lwip_getaddrinfo_ReturnThruPtr_res(&p_addr_info); | ||
auto expected_request = std::array<char,9>{0x04, 0x01, 0x00, 0x50, 0x5a, 0x5a, 0x5a, 0x5a, 0x00 }; | ||
mock_write_Stub(capture_lambda([&test_parent, expected_request, &timeout](esp_transport_handle_t transport, const char *request_sent, int len, int timeout_ms, [[maybe_unused]]int num_call) { | ||
using namespace Catch::Matchers; | ||
REQUIRE(transport == test_parent.get()); | ||
REQUIRE(len == expected_request.size()); | ||
REQUIRE(timeout_ms == timeout); | ||
REQUIRE(std::equal(request_sent,request_sent+len, std::begin(expected_request), std::end(expected_request))); | ||
return len; | ||
})); | ||
|
||
SECTION("Successful connection request") { | ||
|
||
auto proxy_response = make_response(SOCKS_RESPONSE_SUCCESS); | ||
|
||
|
||
mock_read_ExpectAndReturn(test_parent.get(), proxy_response.data(), proxy_response.size(), timeout, proxy_response.size()); | ||
mock_read_IgnoreArg_buffer(); | ||
mock_read_ReturnArrayThruPtr_buffer(proxy_response.data(), proxy_response.size()); | ||
REQUIRE(esp_transport_connect(socks_transport.get(), test_target.data(), target_port, timeout) == 0); | ||
}; | ||
|
||
SECTION("Proxy rejected request") { | ||
auto proxy_response = make_response(SOCKS_RESPONSE_REQUEST_REJECTED); | ||
|
||
mock_read_ExpectAndReturn(test_parent.get(), proxy_response.data(), proxy_response.size(), timeout, proxy_response.size()); | ||
mock_read_IgnoreArg_buffer(); | ||
mock_read_ReturnArrayThruPtr_buffer(proxy_response.data(), proxy_response.size()); | ||
REQUIRE(esp_transport_connect(socks_transport.get(), test_target.data(), target_port, timeout) == -1); | ||
REQUIRE(errno == SOCKS_RESPONSE_REQUEST_REJECTED); | ||
} | ||
|
||
SECTION("Client not running identification protocol") { | ||
auto proxy_response = make_response(SOCKS_RESPONSE_NOT_RUNNING_IDENTD); | ||
|
||
mock_read_ExpectAndReturn(test_parent.get(), proxy_response.data(), proxy_response.size(), timeout, proxy_response.size()); | ||
mock_read_IgnoreArg_buffer(); | ||
mock_read_ReturnArrayThruPtr_buffer(proxy_response.data(), proxy_response.size()); | ||
REQUIRE(esp_transport_connect(socks_transport.get(), test_target.data(), target_port, timeout) == -1); | ||
REQUIRE(errno == SOCKS_RESPONSE_NOT_RUNNING_IDENTD); | ||
} | ||
|
||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
CONFIG_IDF_TARGET="linux" | ||
CONFIG_COMPILER_CXX_EXCEPTIONS=y | ||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n |
61 changes: 61 additions & 0 deletions
61
components/tcp_transport/include/esp_transport_socks_proxy.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#pragma once | ||
|
||
#include <stdint.h> | ||
#include "esp_transport.h" | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
typedef enum socks_version_t {SOCKS4 = 4} socks_version_t; | ||
|
||
typedef enum socks_transport_response_t { | ||
// The following values correspond to transport operation | ||
SOCKS_RESPONSE_TARGET_NOT_FOUND = 0xF0, | ||
SOCKS_RESPONSE_PROXY_UNREACHABLE = 0xF1, | ||
SOCKS_TIMEOUT = 0xF2, | ||
// The following values are defined by the SOCKS4 protocol | ||
SOCKS_RESPONSE_SUCCESS = 0x5a, | ||
SOCKS_RESPONSE_REQUEST_REJECTED = 0x5B, | ||
SOCKS_RESPONSE_NOT_RUNNING_IDENTD = 0x5c, | ||
SOCKS_RESPONSE_COULD_NOT_CONFIRM_ID = 0x5d, | ||
} socks_transport_error_t; | ||
|
||
/* | ||
* Socks configuration structure | ||
*/ | ||
typedef struct esp_transport_socks_proxy_config_t { | ||
const socks_version_t version; /*!< Socks protocol version.*/ | ||
const char *address;/*!< Proxy address*/ | ||
const int port; /*< Proxy port*/ | ||
} esp_transport_socks_proxy_config_t; | ||
|
||
/** | ||
* @brief Create a proxy transport | ||
* @param parent_handle Handle for the parent transport | ||
* @param config Pointer to the configuration structure to use | ||
* | ||
* @return | ||
* - transport Handler for the created transport. | ||
* - NULL in case of failure | ||
*/ | ||
esp_transport_handle_t esp_transport_socks_proxy_init(esp_transport_handle_t parent_handle, const esp_transport_socks_proxy_config_t *config); | ||
|
||
/** | ||
* @brief Changes the configuration of the proxy | ||
* @param socks_transport Handle for the transport | ||
* @param config Pointer to the configuration structure to use | ||
* | ||
* @return | ||
* - ESP_OK on success | ||
*/ | ||
esp_err_t esp_transport_socks_proxy_set_config(esp_transport_handle_t socks_transport, const esp_transport_socks_proxy_config_t *config); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif |
Oops, something went wrong.