mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 06:11:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			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;
 | 
						|
}
 |