You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于隧道技术的HTTPS代理实现疑问及C语言开发需求

HTTPS代理隧道实现详解(基于C语言)

核心疑问解答

  1. 代理如何获取HTTPS目标服务器的IP?
    HTTPS隧道建立的第一步是客户端向代理发送明文的HTTP CONNECT请求,格式类似:CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n。代理可以从这个明文请求里提取目标主机和端口,再通过DNS解析获取IP,不需要解密后续的HTTPS流量。

  2. 无法控制客户端的情况下,代理如何适配?
    标准HTTPS客户端(比如浏览器、curl)在使用HTTP代理时,会自动发送CONNECT请求来建立隧道,你只需要在代理中增加对CONNECT方法的处理逻辑即可,不需要修改客户端。

  3. 加密的HTTPS数据包如何转发?
    隧道建立完成后,代理只需要做透明的双向数据转发:把客户端发来的加密数据直接转发给目标服务器,把目标服务器返回的加密数据直接转发给客户端,全程不需要解密HTTPS流量。

修改后的完整C语言代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>

#define PROXY_PORT 8080
#define BLACKLISTED_URL "example.com"

// 双向转发数据的线程函数
void* forward_data(void* args) {
    int* fds = (int*)args;
    int from_fd = fds[0];
    int to_fd = fds[1];
    char buffer[4096];
    ssize_t bytes_read, bytes_written;

    while ((bytes_read = recv(from_fd, buffer, sizeof(buffer), 0)) > 0) {
        bytes_written = send(to_fd, buffer, bytes_read, 0);
        if (bytes_written < 0) {
            perror("Failed to send data");
            break;
        }
    }

    close(from_fd);
    close(to_fd);
    free(args);
    return NULL;
}

void handle_client(int client_socket) {
    char request[4096];
    ssize_t bytes_received = recv(client_socket, request, sizeof(request)-1, 0);
    if (bytes_received <= 0) {
        perror("Failed to receive request");
        close(client_socket);
        return;
    }
    request[bytes_received] = '\0';

    printf("Received request:\n%s\n", request);

    // 检查是否是CONNECT请求
    if (strncmp(request, "CONNECT ", 8) == 0) {
        // 提取目标主机和端口
        char* target_start = request + 8;
        char* target_end = strstr(target_start, " HTTP/");
        if (target_end == NULL) {
            const char* bad_response = "HTTP/1.1 400 Bad Request\r\n\r\n";
            send(client_socket, bad_response, strlen(bad_response), 0);
            close(client_socket);
            return;
        }
        size_t target_len = target_end - target_start;
        char* target = malloc(target_len + 1);
        if (target == NULL) {
            perror("Failed to allocate memory");
            close(client_socket);
            return;
        }
        strncpy(target, target_start, target_len);
        target[target_len] = '\0';

        // 分割主机和端口,默认443
        char* port_str = strchr(target, ':');
        char* host = target;
        int port = 443;
        if (port_str != NULL) {
            *port_str = '\0';
            port = atoi(port_str + 1);
            if (port <= 0 || port > 65535) {
                port = 443;
            }
        }

        // 黑名单检查
        if (strstr(host, BLACKLISTED_URL) != NULL) {
            const char* forbidden_response = "HTTP/1.1 403 Forbidden\r\nContent-Length: 19\r\n\r\nAccess Denied: URL blocked\r\n";
            send(client_socket, forbidden_response, strlen(forbidden_response), 0);
            free(target);
            close(client_socket);
            return;
        }

        // 解析目标主机IP
        struct addrinfo hints = {0};
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;

        struct addrinfo* addr = NULL;
        int ret = getaddrinfo(host, NULL, &hints, &addr);
        if (ret != 0) {
            perror("Failed to resolve host");
            const char* error_response = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
            send(client_socket, error_response, strlen(error_response), 0);
            free(target);
            close(client_socket);
            return;
        }

        // 建立到目标服务器的连接
        int target_socket = socket(AF_INET, SOCK_STREAM, 0);
        struct sockaddr_in target_addr = *(struct sockaddr_in*)addr->ai_addr;
        target_addr.sin_port = htons(port);
        freeaddrinfo(addr);

        if (connect(target_socket, (struct sockaddr*)&target_addr, sizeof(target_addr)) < 0) {
            perror("Failed to connect to target server");
            const char* error_response = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
            send(client_socket, error_response, strlen(error_response), 0);
            free(target);
            close(target_socket);
            close(client_socket);
            return;
        }

        // 返回连接建立成功的响应
        const char* connect_response = "HTTP/1.1 200 Connection Established\r\n\r\n";
        send(client_socket, connect_response, strlen(connect_response), 0);

        // 创建两个线程分别转发客户端<->目标服务器的数据
        pthread_t client_to_target, target_to_client;
        int* args1 = malloc(2 * sizeof(int));
        int* args2 = malloc(2 * sizeof(int));
        args1[0] = client_socket;
        args1[1] = target_socket;
        args2[0] = target_socket;
        args2[1] = client_socket;

        pthread_create(&client_to_target, NULL, forward_data, args1);
        pthread_create(&target_to_client, NULL, forward_data, args2);

        // 等待线程结束
        pthread_join(client_to_target, NULL);
        pthread_join(target_to_client, NULL);

        free(target);
        return;
    } else {
        // 原有的HTTP代理逻辑
        char* hostStart = strstr(request, "Host:");
        if (hostStart == NULL) {
            printf("No Host field found in the request\n");
            close(client_socket);
            return;
        }

        hostStart += strlen("Host: ");
        char* hostEnd = strchr(hostStart, '\n');
        if (hostEnd == NULL) {
            printf("No end of line found after the Host field\n");
            close(client_socket);
            return;
        }

        size_t urlLength = hostEnd - hostStart;
        char* url = malloc(urlLength + 1);
        if (url == NULL) {
            perror("Error allocating memory for URL");
            close(client_socket);
            return;
        }
        strncpy(url, hostStart, urlLength);
        url[urlLength] = '\0';
        url[strcspn(url, "\r\n")] = '\0';

        printf("Extracted URL: %s\n", url);

        if (strstr(url, BLACKLISTED_URL) != NULL) {
            printf("URL blocked: %s\n", BLACKLISTED_URL);
            const char* response = "HTTP/1.1 403 Forbidden\r\nContent-Length: 19\r\n\r\nAccess Denied: URL blocked\r\n";
            send(client_socket, response, strlen(response), 0);
            free(url);
            close(client_socket);
            return;
        }

        struct addrinfo hints = {0};
        hints.ai_flags = AI_NUMERICHOST;
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;

        struct addrinfo *addr = NULL;
        struct sockaddr_in target;

        int ret = getaddrinfo(url, NULL, &hints, &addr);
        if (ret == EAI_NONAME) {
            hints.ai_flags = 0;
            ret = getaddrinfo(url, NULL, &hints, &addr);
        }
        if (ret != 0) {
            perror("Failed to resolve host");
            const char* error_response = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
            send(client_socket, error_response, strlen(error_response), 0);
            free(url);
            close(client_socket);
            return;
        }

        target = *(struct sockaddr_in*)(addr->ai_addr);
        freeaddrinfo(addr);

        int target_socket = socket(AF_INET, SOCK_STREAM, 0);
        struct sockaddr_in target_addr;
        target_addr.sin_family = AF_INET;
        target_addr.sin_port = htons(80);
        target_addr.sin_addr.s_addr = target.sin_addr.s_addr;

        if (connect(target_socket, (struct sockaddr*)&target_addr, sizeof(target_addr)) < 0) {
            perror("Error connecting to the target server");
            free(url);
            close(target_socket);
            close(client_socket);
            return;
        }

        send(target_socket, request, bytes_received, 0);

        char response_buffer[4096];
        ssize_t bytes_sent;
        while ((bytes_received = recv(target_socket, response_buffer, sizeof(response_buffer), 0)) > 0) {
            bytes_sent = send(client_socket, response_buffer, bytes_received, 0);
            if (bytes_sent < 0) {
                perror("Error sending response to the client");
                break;
            }
        }

        free(url);
        close(client_socket);
        close(target_socket);
    }
}

int main() {
    int proxy_socket = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in proxy_addr;
    proxy_addr.sin_family = AF_INET;
    proxy_addr.sin_port = htons(PROXY_PORT);
    proxy_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡,不止127.0.0.1

    int opt = 1;
    if (setsockopt(proxy_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        perror("Failed to set socket options");
        return 1;
    }

    if (bind(proxy_socket, (struct sockaddr*)&proxy_addr, sizeof(proxy_addr)) < 0) {
        perror("Error binding to the proxy port");
        return 1;
    }

    if (listen(proxy_socket, 10) < 0) {
        perror("Error listening on the proxy socket");
        return 1;
    }

    printf("Proxy server listening on port %d...\n", PROXY_PORT);

    while (1) {
        struct sockaddr_in client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        int client_socket = accept(proxy_socket, (struct sockaddr*)&client_addr, &client_addr_len);

        if (client_socket < 0) {
            perror("Error accepting client connection");
            continue;
        }

        printf("Connection accepted from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        
        // 创建线程处理客户端请求,避免阻塞主循环
        pthread_t client_thread;
        int* socket_ptr = malloc(sizeof(int));
        *socket_ptr = client_socket;
        pthread_create(&client_thread, NULL, (void*)handle_client, socket_ptr);
        pthread_detach(client_thread); // 分离线程,自动释放资源
    }

    close(proxy_socket);
    return 0;
}

代码关键修改说明

  • 新增CONNECT请求处理逻辑:解析目标主机和端口,建立到目标服务器的连接,返回200 Connection Established响应。
  • 实现双向数据转发:使用线程分别处理客户端到目标服务器、目标服务器到客户端的数据流,避免单线程阻塞。
  • 优化主循环:用线程处理每个客户端连接,主循环可以继续接受新连接。
  • 保留原有HTTP代理逻辑:兼容原有的HTTP请求转发功能。

内容的提问来源于stack exchange,提问作者MT 16

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:20:55