mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
d6db90a3cd
- Basic implementation of Socks4 protocol. - Introduce basic host testing.
225 lines
7.8 KiB
C
225 lines
7.8 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <netinet/in.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include "esp_err.h"
|
|
#include "esp_timer.h"
|
|
#include "esp_check.h"
|
|
#include "esp_transport_socks_proxy.h"
|
|
#include "netdb.h"
|
|
#include "string.h"
|
|
|
|
#include "esp_log.h"
|
|
#include "include/esp_transport.h"
|
|
|
|
static const char *TAG = "transport_proxy";
|
|
static const uint32_t SOCKS4_FIX_MESSAGE_SIZE = 8;
|
|
#define SOCKS4_RESPONSE_SIZE 8
|
|
|
|
#define SOCKS_ERROR_IF(cond, err_code, format, ...) do {\
|
|
ESP_GOTO_ON_FALSE(!(cond), err_code, Error, TAG, format , ##__VA_ARGS__);\
|
|
}while(0)
|
|
|
|
typedef enum {CONNECT = 0x01, BIND = 0x02} command_socks_t;
|
|
|
|
typedef struct {
|
|
union {
|
|
char *user_id;
|
|
};
|
|
} socks_authentication_data_t;
|
|
|
|
typedef struct transport_socks_t {
|
|
socks_version_t version;
|
|
char *proxy_address;
|
|
uint16_t proxy_port;
|
|
socks_authentication_data_t authentication;
|
|
esp_transport_handle_t parent;
|
|
} transport_socks_t;
|
|
|
|
typedef struct __attribute((packed)) socks_request {
|
|
uint8_t version;
|
|
uint8_t command;
|
|
uint16_t destination_port;
|
|
uint32_t destination_ip;
|
|
} socks_request_t;
|
|
|
|
typedef struct __attribute((packed)) socks_response {
|
|
uint8_t version;
|
|
uint8_t code;
|
|
uint16_t destination_port;
|
|
uint32_t destination_ip;
|
|
} socks_response_t;
|
|
|
|
static uint32_t get_IP(const char *const host)
|
|
{
|
|
struct addrinfo *address_info;
|
|
struct addrinfo hints;
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
int res = getaddrinfo(host, NULL, &hints, &address_info);
|
|
if (res) {
|
|
// IP_ADDR_ANY equivalent returned as error here because it doesn't make
|
|
// sense to connect to ANY
|
|
return 0;
|
|
}
|
|
uint32_t ip = ((struct sockaddr_in *)address_info->ai_addr)->sin_addr.s_addr;
|
|
freeaddrinfo(address_info);
|
|
return ip;
|
|
}
|
|
|
|
static int64_t get_tick(void)
|
|
{
|
|
return esp_timer_get_time() / (int64_t)1000;
|
|
}
|
|
|
|
static int socks_connect(esp_transport_handle_t transport, const char *const host, int port, int timeout_ms)
|
|
{
|
|
transport_socks_t *socks_transport = esp_transport_get_context_data(transport);
|
|
int64_t initial_tick = get_tick();
|
|
int64_t remaining_time = timeout_ms;
|
|
int ret = 0;
|
|
uint32_t request_message_len = SOCKS4_FIX_MESSAGE_SIZE + 1;
|
|
char *request_message = NULL;
|
|
|
|
int proxy_connected = esp_transport_connect(socks_transport->parent, socks_transport->proxy_address, socks_transport->proxy_port, remaining_time);
|
|
SOCKS_ERROR_IF(proxy_connected == -1, esp_transport_get_errno(socks_transport->parent), "Error connecting to proxy");
|
|
|
|
if (remaining_time > -1) {
|
|
remaining_time = (int64_t)timeout_ms - (get_tick() - initial_tick);
|
|
SOCKS_ERROR_IF(remaining_time < 0, SOCKS_TIMEOUT, "Connection Timeout");
|
|
}
|
|
|
|
socks_request_t request = {};
|
|
request.version = socks_transport->version;
|
|
request.command = CONNECT;
|
|
request.destination_port = htons(port);
|
|
request.destination_ip = get_IP(host);
|
|
SOCKS_ERROR_IF(request.destination_ip == 0, SOCKS_RESPONSE_TARGET_NOT_FOUND, "Failed to resolve target address");
|
|
|
|
if (socks_transport->authentication.user_id) {
|
|
request_message_len += strlen(socks_transport->authentication.user_id);
|
|
request_message = calloc(request_message_len, sizeof(char));
|
|
if (request_message) {
|
|
strcpy(request_message + sizeof(socks_request_t) +1, socks_transport->authentication.user_id);
|
|
}
|
|
} else {
|
|
request_message = calloc(request_message_len, sizeof(char));
|
|
}
|
|
SOCKS_ERROR_IF(request_message == NULL, ESP_ERR_NO_MEM, "Failed to allocate request message");
|
|
memcpy(request_message, (char *)&request, sizeof(socks_request_t));
|
|
|
|
SOCKS_ERROR_IF(esp_transport_write(socks_transport->parent, request_message, request_message_len, remaining_time) < 0, esp_transport_get_errno(socks_transport->parent), "Failed to write the request message");
|
|
|
|
if (remaining_time > -1) {
|
|
remaining_time = (int64_t)timeout_ms - (get_tick() - initial_tick);
|
|
SOCKS_ERROR_IF(remaining_time < 0, SOCKS_TIMEOUT, "Connection Timeout");
|
|
}
|
|
|
|
char proxy_response[SOCKS4_RESPONSE_SIZE];
|
|
SOCKS_ERROR_IF(esp_transport_read(socks_transport->parent, proxy_response, SOCKS4_RESPONSE_SIZE, remaining_time) < 0, esp_transport_get_errno(socks_transport->parent), "Failed to get a response");
|
|
|
|
socks_response_t *response = (socks_response_t *)proxy_response;
|
|
SOCKS_ERROR_IF(response->code != SOCKS_RESPONSE_SUCCESS, response->code, "Request Rejected with : %02x", response->code);
|
|
free(request_message);
|
|
return 0;
|
|
Error:
|
|
free(request_message);
|
|
errno = ret;
|
|
return -1;
|
|
}
|
|
|
|
static int socks_close(esp_transport_handle_t transport)
|
|
{
|
|
transport_socks_t *socks_transport = esp_transport_get_context_data(transport);
|
|
return esp_transport_close(socks_transport->parent);
|
|
|
|
}
|
|
|
|
static int socks_write(esp_transport_handle_t transport, const char *buffer, int len, int timeout_ms)
|
|
{
|
|
transport_socks_t *socks_transport = esp_transport_get_context_data(transport);
|
|
return esp_transport_write(socks_transport->parent, buffer, len, timeout_ms);
|
|
}
|
|
|
|
static int socks_read(esp_transport_handle_t transport, char *buffer, int len, int timeout_ms)
|
|
{
|
|
transport_socks_t *socks_transport = esp_transport_get_context_data(transport);
|
|
return esp_transport_read(socks_transport->parent, buffer, len, timeout_ms);
|
|
}
|
|
|
|
static int socks_poll_read(esp_transport_handle_t transport, int timeout_ms)
|
|
{
|
|
transport_socks_t *socks_transport = esp_transport_get_context_data(transport);
|
|
return esp_transport_poll_read(socks_transport->parent, timeout_ms);
|
|
}
|
|
|
|
static int socks_poll_write(esp_transport_handle_t transport, int timeout_ms)
|
|
{
|
|
transport_socks_t *socks_transport = esp_transport_get_context_data(transport);
|
|
return esp_transport_poll_write(socks_transport->parent, timeout_ms);
|
|
}
|
|
|
|
static esp_err_t socks_destroy(esp_transport_handle_t transport)
|
|
{
|
|
if (transport == NULL) {
|
|
return ESP_OK;
|
|
}
|
|
transport_socks_t *socks_transport = esp_transport_get_context_data(transport);
|
|
if (socks_transport == NULL) {
|
|
return ESP_FAIL;
|
|
}
|
|
free(socks_transport->proxy_address);
|
|
free(socks_transport);
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
static esp_err_t check_parameters(esp_transport_handle_t parent_handle, const esp_transport_socks_proxy_config_t *config)
|
|
{
|
|
if (parent_handle == NULL) {
|
|
ESP_LOGE(TAG, "Parent transport is invalid");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
if (config == NULL || config->address == NULL) {
|
|
ESP_LOGE(TAG, "Invalid Configuration");
|
|
return ESP_FAIL;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_transport_handle_t esp_transport_socks_proxy_init(esp_transport_handle_t parent_handle, const esp_transport_socks_proxy_config_t *config)
|
|
{
|
|
if (check_parameters(parent_handle, config) == ESP_FAIL) {
|
|
return NULL;
|
|
};
|
|
|
|
int ret = 0;
|
|
|
|
esp_transport_handle_t transport = esp_transport_init();
|
|
SOCKS_ERROR_IF(transport == NULL, ESP_ERR_NO_MEM, "Failed to allocate transport");
|
|
|
|
transport_socks_t *socks_context = calloc(1, sizeof(transport_socks_t));
|
|
SOCKS_ERROR_IF(socks_context == NULL, ESP_ERR_NO_MEM, "Failed to allocate transport context");
|
|
esp_transport_set_context_data(transport, socks_context);
|
|
esp_transport_set_func(transport, socks_connect, socks_read, socks_write, socks_close, socks_poll_read, socks_poll_write, socks_destroy);
|
|
|
|
socks_context->parent = parent_handle;
|
|
socks_context->proxy_address = strdup(config->address);
|
|
SOCKS_ERROR_IF(socks_context->proxy_address == NULL, ESP_ERR_NO_MEM, "Failed to copy proxy address");
|
|
socks_context->proxy_port = config->port;
|
|
socks_context->version = config->version;
|
|
|
|
|
|
return transport;
|
|
Error:
|
|
esp_transport_destroy(transport);
|
|
errno = ret;
|
|
return NULL;
|
|
}
|