mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-17 20:42:21 +02:00
EXAMPLES/ASIO: Adds a SOCKS4 example
Creates an example on how to connect using Socks4 based proxy. * Original commit: espressif/esp-idf@f7b842bbc7
This commit is contained in:
committed by
gabsuren
parent
b76d3fbff9
commit
30dae8f7ed
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: CC0-1.0
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
*
|
*
|
||||||
@ -96,7 +96,7 @@ public:
|
|||||||
* @tparam completion_handler A callable to act as the final handler for the process.
|
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||||
* @param host host address
|
* @param host host address
|
||||||
* @param port port number - due to a limitation on lwip implementation this should be the number not the
|
* @param port port number - due to a limitation on lwip implementation this should be the number not the
|
||||||
* service name tipically seen in ASIO examples.
|
* service name typically seen in ASIO examples.
|
||||||
*
|
*
|
||||||
* @note The class could be modified to store the completion handler, as a member variable, instead of
|
* @note The class could be modified to store the completion handler, as a member variable, instead of
|
||||||
* pass it along asynchronous calls to allow the process to run again completely.
|
* pass it along asynchronous calls to allow the process to run again completely.
|
||||||
|
10
examples/protocols/asio/socks4/CMakeLists.txt
Normal file
10
examples/protocols/asio/socks4/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
# (Not part of the boilerplate)
|
||||||
|
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||||
|
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(asio_sock4)
|
73
examples/protocols/asio/socks4/README.md
Normal file
73
examples/protocols/asio/socks4/README.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-S2 |
|
||||||
|
| ----------------- | ----- | ----- |
|
||||||
|
|
||||||
|
# Async request using ASIO
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
The application aims to show how to connect to a Socks4 proxy using async operations with ASIO. The SOCKS protocol is
|
||||||
|
briefly described by the diagram below.
|
||||||
|
|
||||||
|
┌──────┐ ┌─────┐ ┌──────┐
|
||||||
|
│Client│ │Proxy│ │Target│
|
||||||
|
└──┬───┘ └──┬──┘ └──┬───┘
|
||||||
|
│ │ │
|
||||||
|
│ ╔═╧══════════════╗ │
|
||||||
|
══════════════════════╪════════════════════════╣ Initialization ╠═══╪════════════════════════════════════════════
|
||||||
|
│ ╚═╤══════════════╝ │
|
||||||
|
│ │ │
|
||||||
|
╔══════════════════╗│ │ │
|
||||||
|
║We establish a ░║│ Socket Connection │ │
|
||||||
|
║TCP connection ║│ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ > │
|
||||||
|
║and get a socket ║│ │ │
|
||||||
|
╚══════════════════╝│ │ │
|
||||||
|
│ │ │
|
||||||
|
│ ╔═╧══════════════╗ │
|
||||||
|
══════════════════════╪════════════════════════╣ Socks Protocol ╠═══╪════════════════════════════════════════════
|
||||||
|
│ ╚═╤══════════════╝ │
|
||||||
|
│ │ │
|
||||||
|
│ Client Connection Request│ │
|
||||||
|
│ ─────────────────────────> │
|
||||||
|
│ │ │
|
||||||
|
│ │ │
|
||||||
|
│ ╔════════════╪═══════╤══════════╪════════════════════════════════╗
|
||||||
|
│ ║ TARGET CONNECTION │ │ ║
|
||||||
|
│ ╟────────────────────┘ │ ╔═══════════════════╗ ║
|
||||||
|
│ ║ │ Socket Connection│ ║Proxy establishes ░║ ║
|
||||||
|
│ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ > ║ TCPconnection ║ ║
|
||||||
|
│ ║ │ │ ║ with target host ║ ║
|
||||||
|
│ ╚════════════╪══════════════════╪══╚═══════════════════╝═════════╝
|
||||||
|
│ │ │
|
||||||
|
│ Response packet │ │
|
||||||
|
│ <───────────────────────── │
|
||||||
|
│ │ │
|
||||||
|
│ │ │
|
||||||
|
│ │ ╔═══════╗ │
|
||||||
|
══════════════════════╪══════════════════════════╪══╣ Usage ╠═══════╪════════════════════════════════════════════
|
||||||
|
│ │ ╚═══════╝ │
|
||||||
|
│ │ │
|
||||||
|
╔═════════════════╗│ │ │
|
||||||
|
║Client uses the ░║│ │ │
|
||||||
|
║ socket opened ║│ │ │
|
||||||
|
║ with proxy ║│ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
|
||||||
|
║to communicate ║│ │ │
|
||||||
|
║ ║│ │ │
|
||||||
|
╚═════════════════╝┴───┐ ┌──┴──┐ ┌──┴───┐
|
||||||
|
│Client│ │Proxy│ │Target│
|
||||||
|
└──────┘ └─────┘ └──────┘
|
||||||
|
|
||||||
|
|
||||||
|
# Configure and Building example
|
||||||
|
|
||||||
|
This example requires the proxy address to be configured. You can do this using the menuconfig option.
|
||||||
|
Proxy address and port must be configured in order for this example to work.
|
||||||
|
|
||||||
|
If using Linux ssh can be used as a proxy for testing.
|
||||||
|
|
||||||
|
```
|
||||||
|
ssh -N -v -D 0.0.0.0:1080 localhost
|
||||||
|
```
|
||||||
|
# Async operations composition and automatic lifetime control
|
||||||
|
|
||||||
|
For documentation about the structure of this example look into [async\_request README](../async_request/README.md).
|
||||||
|
|
2
examples/protocols/asio/socks4/main/CMakeLists.txt
Normal file
2
examples/protocols/asio/socks4/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "socks4.cpp"
|
||||||
|
INCLUDE_DIRS ".")
|
16
examples/protocols/asio/socks4/main/Kconfig.projbuild
Normal file
16
examples/protocols/asio/socks4/main/Kconfig.projbuild
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
config EXAMPLE_PROXY_ADDRESS
|
||||||
|
string "Proxy address"
|
||||||
|
default "myproxy"
|
||||||
|
help
|
||||||
|
Address of the proxy to be used.
|
||||||
|
|
||||||
|
config EXAMPLE_PROXY_PORT
|
||||||
|
string "Proxy port"
|
||||||
|
default "myport"
|
||||||
|
help
|
||||||
|
Port for the proxy. Due to a limitation of lwip, must
|
||||||
|
be a number e.g. "1080".
|
||||||
|
|
||||||
|
endmenu
|
393
examples/protocols/asio/socks4/main/socks4.cpp
Normal file
393
examples/protocols/asio/socks4/main/socks4.cpp
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ASIO Socks4 example
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
#include <asio.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <system_error>
|
||||||
|
#include <utility>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "socks4.hpp"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "protocol_examples_common.h"
|
||||||
|
|
||||||
|
constexpr auto TAG = "asio_socks4";
|
||||||
|
using asio::ip::tcp;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void esp_init()
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
esp_log_level_set("async_request", ESP_LOG_DEBUG);
|
||||||
|
|
||||||
|
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||||
|
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||||
|
* examples/protocols/README.md for more information about this function.
|
||||||
|
*/
|
||||||
|
ESP_ERROR_CHECK(example_connect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simple class to add the resolver to a chain of actions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AddressResolution : public std::enable_shared_from_this<AddressResolution> {
|
||||||
|
public:
|
||||||
|
explicit AddressResolution(asio::io_context &context) : ctx(context), resolver(ctx) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initiator function for the address resolution
|
||||||
|
*
|
||||||
|
* @tparam CompletionToken callable responsible to use the results.
|
||||||
|
*
|
||||||
|
* @param host Host address
|
||||||
|
* @param port Port for the target, must be number due to a limitation on lwip.
|
||||||
|
*/
|
||||||
|
template<class CompletionToken>
|
||||||
|
void resolve(const std::string &host, const std::string &port, CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
auto self(shared_from_this());
|
||||||
|
resolver.async_resolve(host, port, [self, completion_handler](const asio::error_code & error, tcp::resolver::results_type results) {
|
||||||
|
if (error) {
|
||||||
|
ESP_LOGE(TAG, "Failed to resolve: %s", error.message().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completion_handler(self, results);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
asio::io_context &ctx;
|
||||||
|
tcp::resolver resolver;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connection class
|
||||||
|
*
|
||||||
|
* The lowest level dependency on our asynchronous task, Connection provide an interface to TCP sockets.
|
||||||
|
* A similar class could be provided for a TLS connection.
|
||||||
|
*
|
||||||
|
* @note: All read and write operations are written on an explicit strand, even though an implicit strand
|
||||||
|
* occurs in this example since we run the io context in a single task.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Connection : public std::enable_shared_from_this<Connection> {
|
||||||
|
public:
|
||||||
|
explicit Connection(asio::io_context &context) : ctx(context), strand(context), socket(ctx) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start the connection
|
||||||
|
*
|
||||||
|
* Async operation to start a connection. As the final act of the process the Connection class pass a
|
||||||
|
* std::shared_ptr of itself to the completion_handler.
|
||||||
|
* Since it uses std::shared_ptr as an automatic control of its lifetime this class must be created
|
||||||
|
* through a std::make_shared call.
|
||||||
|
*
|
||||||
|
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||||
|
* @param host host address
|
||||||
|
* @param port port number - due to a limitation on lwip implementation this should be the number not the
|
||||||
|
* service name typically seen in ASIO examples.
|
||||||
|
*
|
||||||
|
* @note The class could be modified to store the completion handler, as a member variable, instead of
|
||||||
|
* pass it along asynchronous calls to allow the process to run again completely.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template<class CompletionToken>
|
||||||
|
void start(tcp::resolver::results_type results, CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
connect(results, completion_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start an async write on the socket
|
||||||
|
*
|
||||||
|
* @tparam data
|
||||||
|
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template<class DataType, class CompletionToken>
|
||||||
|
void write_async(const DataType &data, CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
asio::async_write(socket, data, asio::bind_executor(strand, completion_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start an async read on the socket
|
||||||
|
*
|
||||||
|
* @tparam data
|
||||||
|
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template<class DataBuffer, class CompletionToken>
|
||||||
|
void read_async(DataBuffer &&in_data, CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
asio::async_read(socket, in_data, asio::bind_executor(strand, completion_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
template<class CompletionToken>
|
||||||
|
void connect(tcp::resolver::results_type results, CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
auto self(shared_from_this());
|
||||||
|
asio::async_connect(socket, results, [self, completion_handler](const asio::error_code & error, [[maybe_unused]] const tcp::endpoint & endpoint) {
|
||||||
|
if (error) {
|
||||||
|
ESP_LOGE(TAG, "Failed to connect: %s", error.message().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completion_handler(self);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
asio::io_context &ctx;
|
||||||
|
asio::io_context::strand strand;
|
||||||
|
tcp::socket socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Socks {
|
||||||
|
|
||||||
|
struct ConnectionData {
|
||||||
|
ConnectionData(socks4::request::command_type cmd, const asio::ip::tcp::endpoint &endpoint,
|
||||||
|
const std::string &user_id) : request(cmd, endpoint, user_id) {};
|
||||||
|
socks4::request request;
|
||||||
|
socks4::reply reply;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class CompletionToken>
|
||||||
|
void async_connect(asio::io_context &context, std::string proxy, std::string proxy_port, std::string host, std::string port, CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The first step is to resolve the address of the proxy we want to connect to.
|
||||||
|
* The AddressResolution itself is injected to the completion handler.
|
||||||
|
*/
|
||||||
|
// Resolve proxy
|
||||||
|
std::make_shared<AddressResolution>(context)->resolve(proxy, proxy_port,
|
||||||
|
[&context, host, port, completion_handler](std::shared_ptr<AddressResolution> resolver, tcp::resolver::results_type proxy_resolution) {
|
||||||
|
// We also need to resolve the target host address
|
||||||
|
resolver->resolve(host, port, [&context, proxy_resolution, completion_handler](std::shared_ptr<AddressResolution> resolver, tcp::resolver::results_type host_resolution) {
|
||||||
|
// Make connection with the proxy
|
||||||
|
ESP_LOGI(TAG, "Startig Proxy Connection");
|
||||||
|
std::make_shared<Connection>(context)->start(proxy_resolution,
|
||||||
|
[resolver, host_resolution, completion_handler](std::shared_ptr<Connection> connection) {
|
||||||
|
auto connect_data = std::make_shared<ConnectionData>(socks4::request::connect, *host_resolution, "");
|
||||||
|
ESP_LOGI(TAG, "Sending Request to proxy for host connection.");
|
||||||
|
connection->write_async(connect_data->request.buffers(), [connection, connect_data, completion_handler](std::error_code error, std::size_t bytes_received) {
|
||||||
|
if (error) {
|
||||||
|
ESP_LOGE(TAG, "Proxy request write error: %s", error.message().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connection->read_async(connect_data->reply.buffers(), [connection, connect_data, completion_handler](std::error_code error, std::size_t bytes_received) {
|
||||||
|
if (error) {
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "Proxy response read error: %s", error.message().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!connect_data->reply.success()) {
|
||||||
|
ESP_LOGE(TAG, "Proxy error: %#x", connect_data->reply.status());
|
||||||
|
}
|
||||||
|
completion_handler(connection);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
} // namespace Socks
|
||||||
|
|
||||||
|
namespace Http {
|
||||||
|
enum class Method { GET };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simple HTTP request class
|
||||||
|
*
|
||||||
|
* The user needs to write the request information direct to header and body fields.
|
||||||
|
*
|
||||||
|
* Only GET verb is provided.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Request {
|
||||||
|
public:
|
||||||
|
Request(Method method, std::string host, std::string port, const std::string &target) : host_data(std::move(host)), port_data(std::move(port))
|
||||||
|
{
|
||||||
|
header_data.append("GET ");
|
||||||
|
header_data.append(target);
|
||||||
|
header_data.append(" HTTP/1.1");
|
||||||
|
header_data.append("\r\n");
|
||||||
|
header_data.append("Host: ");
|
||||||
|
header_data.append(host_data);
|
||||||
|
header_data.append("\r\n");
|
||||||
|
header_data.append("\r\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
void set_header_field(std::string const &field)
|
||||||
|
{
|
||||||
|
header_data.append(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_to_body(std::string const &data)
|
||||||
|
{
|
||||||
|
body_data.append(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string &host() const
|
||||||
|
{
|
||||||
|
return host_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &service_port() const
|
||||||
|
{
|
||||||
|
return port_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &header() const
|
||||||
|
{
|
||||||
|
return header_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &body() const
|
||||||
|
{
|
||||||
|
return body_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string host_data;
|
||||||
|
std::string port_data;
|
||||||
|
std::string header_data;
|
||||||
|
std::string body_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simple HTTP response class
|
||||||
|
*
|
||||||
|
* The response is built from received data and only parsed to split header and body.
|
||||||
|
*
|
||||||
|
* A copy of the received data is kept.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct Response {
|
||||||
|
/**
|
||||||
|
* @brief Construct a response from a contiguous buffer.
|
||||||
|
*
|
||||||
|
* Simple http parsing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template<class DataIt>
|
||||||
|
explicit Response(DataIt data, size_t size)
|
||||||
|
{
|
||||||
|
raw_response = std::string(data, size);
|
||||||
|
|
||||||
|
auto header_last = raw_response.find("\r\n\r\n");
|
||||||
|
if (header_last != std::string::npos) {
|
||||||
|
header = raw_response.substr(0, header_last);
|
||||||
|
}
|
||||||
|
body = raw_response.substr(header_last + 3);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Print response content.
|
||||||
|
*/
|
||||||
|
void print()
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Header :\n %s", header.c_str());
|
||||||
|
ESP_LOGI(TAG, "Body : \n %s", body.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string raw_response;
|
||||||
|
std::string header;
|
||||||
|
std::string body;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief HTTP Session
|
||||||
|
*
|
||||||
|
* Session class to handle HTTP protocol implementation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Session : public std::enable_shared_from_this<Session> {
|
||||||
|
public:
|
||||||
|
explicit Session(std::shared_ptr<Connection> connection_in) : connection(std::move(connection_in))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CompletionToken>
|
||||||
|
void send_request(const Request &request, CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
auto self = shared_from_this();
|
||||||
|
send_data = { asio::buffer(request.header()), asio::buffer(request.body()) };
|
||||||
|
connection->write_async(send_data, [self, &completion_handler](std::error_code error, std::size_t bytes_transfered) {
|
||||||
|
if (error) {
|
||||||
|
ESP_LOGE(TAG, "Request write error: %s", error.message().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Bytes Transfered: %d", bytes_transfered);
|
||||||
|
self->get_response(completion_handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class CompletionToken>
|
||||||
|
void get_response(CompletionToken &&completion_handler)
|
||||||
|
{
|
||||||
|
auto self = shared_from_this();
|
||||||
|
connection->read_async(asio::buffer(receive_buffer), [self, &completion_handler](std::error_code error, std::size_t bytes_received) {
|
||||||
|
if (error and error.value() != asio::error::eof) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Bytes Received: %d", bytes_received);
|
||||||
|
if (bytes_received == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Response response(std::begin(self->receive_buffer), bytes_received);
|
||||||
|
|
||||||
|
completion_handler(self, response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* For this example we assumed 2048 to be enough for the receive_buffer
|
||||||
|
*/
|
||||||
|
std::array<char, 2048> receive_buffer;
|
||||||
|
/*
|
||||||
|
* The hardcoded 2 below is related to the type we receive the data to send. We gather the parts from Request, header
|
||||||
|
* and body, to send avoiding the copy.
|
||||||
|
*/
|
||||||
|
std::array<asio::const_buffer, 2> send_data;
|
||||||
|
std::shared_ptr<Connection> connection;
|
||||||
|
};
|
||||||
|
}// namespace Http
|
||||||
|
|
||||||
|
extern "C" void app_main(void)
|
||||||
|
{
|
||||||
|
// Basic initialization of ESP system
|
||||||
|
esp_init();
|
||||||
|
|
||||||
|
asio::io_context io_context;
|
||||||
|
Http::Request request(Http::Method::GET, "www.httpbin.org", "80", "/get");
|
||||||
|
Socks::async_connect(io_context, CONFIG_EXAMPLE_PROXY_ADDRESS, CONFIG_EXAMPLE_PROXY_PORT, request.host(), request.service_port(),
|
||||||
|
[&request](std::shared_ptr<Connection> connection) {
|
||||||
|
// Now we create a HTTP::Session and inject the necessary connection.
|
||||||
|
std::make_shared<Http::Session>(connection)->send_request(request, [](std::shared_ptr<Http::Session> session, Http::Response response) {
|
||||||
|
response.print();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// io_context.run will block until all the tasks on the context are done.
|
||||||
|
io_context.run();
|
||||||
|
ESP_LOGI(TAG, "Context run done");
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(example_disconnect());
|
||||||
|
}
|
143
examples/protocols/asio/socks4/main/socks4.hpp
Normal file
143
examples/protocols/asio/socks4/main/socks4.hpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
//
|
||||||
|
// socks4.hpp
|
||||||
|
// ~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SOCKS4_HPP
|
||||||
|
#define SOCKS4_HPP
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <asio/buffer.hpp>
|
||||||
|
#include <asio/ip/tcp.hpp>
|
||||||
|
|
||||||
|
namespace socks4 {
|
||||||
|
|
||||||
|
const unsigned char version = 0x04;
|
||||||
|
|
||||||
|
class request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum command_type
|
||||||
|
{
|
||||||
|
connect = 0x01,
|
||||||
|
bind = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
request(command_type cmd, const asio::ip::tcp::endpoint& endpoint,
|
||||||
|
const std::string& user_id)
|
||||||
|
: version_(version),
|
||||||
|
command_(cmd),
|
||||||
|
user_id_(user_id),
|
||||||
|
null_byte_(0)
|
||||||
|
{
|
||||||
|
// Only IPv4 is supported by the SOCKS 4 protocol.
|
||||||
|
if (endpoint.protocol() != asio::ip::tcp::v4())
|
||||||
|
{
|
||||||
|
throw asio::system_error(
|
||||||
|
asio::error::address_family_not_supported);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert port number to network byte order.
|
||||||
|
unsigned short port = endpoint.port();
|
||||||
|
port_high_byte_ = (port >> 8) & 0xff;
|
||||||
|
port_low_byte_ = port & 0xff;
|
||||||
|
|
||||||
|
// Save IP address in network byte order.
|
||||||
|
address_ = endpoint.address().to_v4().to_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<asio::const_buffer, 7> buffers() const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{
|
||||||
|
asio::buffer(&version_, 1),
|
||||||
|
asio::buffer(&command_, 1),
|
||||||
|
asio::buffer(&port_high_byte_, 1),
|
||||||
|
asio::buffer(&port_low_byte_, 1),
|
||||||
|
asio::buffer(address_),
|
||||||
|
asio::buffer(user_id_),
|
||||||
|
asio::buffer(&null_byte_, 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char version_;
|
||||||
|
unsigned char command_;
|
||||||
|
unsigned char port_high_byte_;
|
||||||
|
unsigned char port_low_byte_;
|
||||||
|
asio::ip::address_v4::bytes_type address_;
|
||||||
|
std::string user_id_;
|
||||||
|
unsigned char null_byte_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class reply
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum status_type
|
||||||
|
{
|
||||||
|
request_granted = 0x5a,
|
||||||
|
request_failed = 0x5b,
|
||||||
|
request_failed_no_identd = 0x5c,
|
||||||
|
request_failed_bad_user_id = 0x5d
|
||||||
|
};
|
||||||
|
|
||||||
|
reply()
|
||||||
|
: null_byte_(0),
|
||||||
|
status_()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<asio::mutable_buffer, 5> buffers()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{
|
||||||
|
asio::buffer(&null_byte_, 1),
|
||||||
|
asio::buffer(&status_, 1),
|
||||||
|
asio::buffer(&port_high_byte_, 1),
|
||||||
|
asio::buffer(&port_low_byte_, 1),
|
||||||
|
asio::buffer(address_)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success() const
|
||||||
|
{
|
||||||
|
return null_byte_ == 0 && status_ == request_granted;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char status() const
|
||||||
|
{
|
||||||
|
return status_;
|
||||||
|
}
|
||||||
|
|
||||||
|
asio::ip::tcp::endpoint endpoint() const
|
||||||
|
{
|
||||||
|
unsigned short port = port_high_byte_;
|
||||||
|
port = (port << 8) & 0xff00;
|
||||||
|
port = port | port_low_byte_;
|
||||||
|
|
||||||
|
asio::ip::address_v4 address(address_);
|
||||||
|
|
||||||
|
return asio::ip::tcp::endpoint(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char null_byte_;
|
||||||
|
unsigned char status_;
|
||||||
|
unsigned char port_high_byte_;
|
||||||
|
unsigned char port_low_byte_;
|
||||||
|
asio::ip::address_v4::bytes_type address_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace socks4
|
||||||
|
|
||||||
|
#endif // SOCKS4_HPP
|
3
examples/protocols/asio/socks4/sdkconfig.defaults
Normal file
3
examples/protocols/asio/socks4/sdkconfig.defaults
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||||
|
CONFIG_COMPILER_CXX_RTTI=y
|
||||||
|
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
|
Reference in New Issue
Block a user