HTTPS 代理永远无法成功连接

HTTPS proxy never manages to connect successfully

我正在尝试做的是处理 HTTP(S) 连接的分叉代理:当 GET(没有 SSL)请求成功执行并且内容被传送到客户端时,当它出现时CONNECT 方法进展不顺利,因为 connect() 到远程服务器可能不会立即成功:事实上,它永远不会成功。

我尝试了一个连接到远程服务器的非阻塞套接字,所以我可以看到 connect() 是立即运行还是需要一些时间:在第二种情况下,我会调用 select() 到查看远程服务器何时准备好向我发送数据:然而,connect() 从未连接。

这是我的代理 main() 代码:

int main(int argc, char *argv[]) {
    // ClientManager.cpp is described below
    ClientManager cm;
    //listening on port given by argv
    if (cm.startListeningForClient(listening_port)) {
        while(true) {
            int new_client_socket = cm.acceptConnectionFromClient();
            if (new_client_socket >= 0) {
                cm.forkAndManageClient();
            }
            else {
                perror("accept error");
            }
        }
    } else {
        perror("Error on start listening");
    }
    return EXIT_SUCCESS;
}

下面是一些与我的问题无关的遗漏,ClientManager.cpp,其函数在上面的 main() 中被调用:

ClientManager::ClientManager() {
    sockfd_client = -1; // socket connected to client
    new_sockfd_client = -1; // socket accepting connection from client
    sockfd_server = -1; // socket connected to remote server
}

// error controls omitted
bool ClientManager::startListeningForClient(int port) {
    struct sockaddr_in serv_addr;
    bzero((char*)&serv_addr,sizeof(serv_addr));

    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(port);
    serv_addr.sin_addr.s_addr=INADDR_ANY;

    sockfd_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    bind(sockfd_client,(struct sockaddr*)&serv_addr, sizeof(serv_addr));
    listen(sockfd_client, 50);
    return true;
}

// error controls omitted
int ClientManager::acceptConnectionFromClient(void) {
    struct sockaddr_in cli_addr;
    unsigned int clilen;
    bzero((char*)&cli_addr, sizeof(cli_addr));
    clilen = sizeof(cli_addr);
    new_sockfd_client = accept(sockfd_client, (struct sockaddr*)&cli_addr, &clilen);
    return new_sockfd_client;
}

int ClientManager::forkAndManageClient() {
    // getRequestFromClient: the method below receives requests from 
    // clients and parses the infos I need (i.e. what method, 
    // hostname of remote server to be resolved, its port, ...) 
    getRequestFromClient();
    // managing the HTTP(S) request by the child process
    int pid = fork();
    if (pid < 0) {
        perror("ERROR on fork");
    }
    else if (pid > 0) {
        // parent process
        // do nothing
    }
    else {
        // close immediately the client socket used for accepting new connections from the parent process
        close (sockfd_client);
        if (!manageRequest()) {
            perror("Error managing request from client");
        }
        // close the connection from the client
        close (new_sockfd_client);
        new_sockfd_client = -1;
        // the child process will terminate now
        _exit(EXIT_SUCCESS);
    }
    return pid;
}

// now the problematic method...
bool ClientManager::manageRequest(void) {
    // if this is a CONNECT request
    if (rm.isCONNECT()) { 
        struct sockaddr_in remote_server;
        int conn_res;
        remote_server.sin_family = AF_INET;
        remote_server.sin_addr.s_addr = rm.getServerAddr();
        remote_server.sin_port = rm.getServerPort();
        fd_set fdset;
        struct timeval tv;

        sockfd_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        // make socket not blocking
        int flags = fcntl(sockfd_server, F_GETFL, 0);
        flags = flags | O_NONBLOCK;
        if (fcntl(sockfd_server, F_SETFL, flags) == -1) {
            perror("FCNTL:");
        }
        printf("CONNECT set socket to non-blocking mode\n");

        conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));

        printf("AFTER CONNECT()\n");
        if (conn_res < 0) {
            if (errno != EINPROGRESS) {
                printf("CONNECT: connect() failed, quitting\n");
                return false;
            }
        }
        printf("CONNECT connection is taking place...\n");

        // connected immediately
        if (conn_res == 0) {
            printf("CONNECT connected OK!\n");
            goto CONNECTED;
        }

        FD_ZERO(&fdset);
        FD_SET(sockfd_server, &fdset);
        tv.tv_sec = 5; // tried 5, 20, 60 seconds, but it always times out
        tv.tv_usec = 0;

        printf("CONNECT attempting select()\n");
        if (select(sockfd_server+1, NULL, &fdset, NULL, &tv) == 0) {
            errno = ETIMEDOUT;
            close(sockfd_server);
            sockfd_server = -1;
            return false;
        }

        if (FD_ISSET(sockfd_server, &fdset)) {
            int so_error;
            socklen_t len = sizeof so_error;
            if (getsockopt(sockfd_server, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0) {
                return false;
            } 
        } else {
            printf("sockfd_server not set\n");
        }

        CONNECTED:
        fcntl(sockfd_server, F_SETFL, flags &~ O_NONBLOCK);
        // yeah, now I will start to deal the HTTPS flow in both directions
        return true;
    }
}

它确实管理将套接字设置为非阻塞模式,并打印 CONNECT connection is taking place...,但它总是 returns Error managing request from client: Connection timed out.

对于发布数英里的 LOC,我深表歉意,但这几天让我发疯,在阅读帖子、教程和指南后,我真的不知道该怎么做。

它现在可以连接到每个需要 HTTPS 连接的站点! 缺少正确的错误检查和套接字描述符的后续关闭。这是我的代码:

bool ClientManager::manageRequest(void) {

if (rm.isCONNECT()) {
    struct sockaddr_in remote_server, local_bind;
    int conn_res, select_res;

    memset(&remote_server, 0, sizeof(remote_server));
    remote_server.sin_family = AF_INET;
    remote_server.sin_addr.s_addr = rm.getServerAddr();
    remote_server.sin_port = rm.getServerPort();

    memset(&local_bind, 0, sizeof(local_bind));
    local_bind.sin_family = AF_INET;
    local_bind.sin_addr.s_addr = htonl(INADDR_ANY);
    local_bind.sin_port = htons(0);

    fd_set rdset, wrset;
    struct timeval tv;

    sockfd_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd_server < 0) {
        perror("socket: ");
    }

    if(!setNonBlocking(sockfd_server))
        perror("fcntl");
    debug_green("CONNECT set socket to non-blocking mode\n");

    bind(sockfd_server, (struct sockaddr*) &local_bind, sizeof(local_bind));

    conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
    // The socket is nonblocking and the connection cannot be completed immediately
    // check for EINPROGRESS
    if ((conn_res == -1) && (errno != EINPROGRESS)) {

        FD_ZERO(&rdset);
        FD_SET(sockfd_server, &rdset);
        wrset = rdset;
        tv.tv_sec = 0;
        tv.tv_usec = 0;

        debug_yellow("CONNECT attempting select()\n");
        do {
            select_res = select(sockfd_server+1, &rdset, &wrset, NULL, &tv);
        } while ((select_res == -1) && (errno == EINTR));

        if ((!FD_ISSET(sockfd_server, &rdset)) && ((!FD_ISSET(sockfd_server, &wrset)))) {
            debug_red("SELECT sockfds not responding\n");
            close(sockfd_server);
            sockfd_server = -1;
            return false;
        }

        conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
        if (conn_res == -1)  {
            if(errno == EISCONN)
                printf ("connect(): connections already existing, OK\n");
            else  {
                printf("connect() for safety check: connection NOT successfull\n");
                close(sockfd_server);
                sockfd_server = -1;
                return false;
            }
        }
        printf("connection OK\n");
        fflush(stdout);
    } else {
        debug_green("Connection immediately OK\n");
        fflush(stdout);
    }
    if (!setBlocking(sockfd_server)) {
        perror("FCNTL:");
    }
    debug_green("CONNECT set socket back to blocking mode\n");fflush(stdout);   
}

return true;
}

函数设置阻塞或非阻塞套接字:

bool ClientManager::setNonBlocking(int sockfd) {
    printf("setting non block socket\n"); fflush(stdout);
    int flags;
    if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
        return false;
    flags |= O_NONBLOCK;
    if (fcntl(sockfd, F_SETFL, flags) < 0)
        return false;
    return true;
}

bool ClientManager::setBlocking(int sockfd) {
    printf("setting block socket\n"); fflush(stdout);
    int flags;
    if ((flags = fcntl(sockfd, F_GETFL, 0)) < 0)
        return false;
    flags &= (~O_NONBLOCK);
    if (fcntl(sockfd, F_SETFL, flags) < 0)
        return false;
    return true;
}

调试函数:

#define DEFAULTCOLOR    "3[0m"
#define RED             "3[22;31m"
#define YELLOW          "3[1;33m"
#define GREEN           "3[0;0;32m"

#define debug_red(...) std::cout << RED << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);
#define debug_yellow(...) std::cout << YELLOW << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);
#define debug_green(...) std::cout << GREEN << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);