代理HTTPS永远不会从客户端或服务器接收数据

时间:2016-04-01 11:00:36

标签: c++ sockets networking proxy http-proxy

我的代理HTTPS处理来自客户端的CONNECT HTTP请求,执行以下步骤:

  1. 打开一个到远程服务器的套接字;
  2. 将套接字设置为非阻塞模式;
  3. 尝试connect()检查if ((connect_res == -1) && (errno != EINPROGRESS));
  4. 循环,select()检查服务器套接字是否已准备好发送或接收数据;
  5. 将套接字设置回阻止模式;
  6. 在成功"HTTP/1.1 200 Connection established\r\n\r\n";
  7. 后向客户端发送connect()
  8. 调用proxyHTTPS();

    // if client sent a CONNECT request...
    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: ");
    }
    
    // make socket not blocking
    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)) {
        debug_yellow("CONNECT attempting select()\n");
        do {
            FD_ZERO(&rdset);
            FD_SET(sockfd_server, &rdset);
            wrset = rdset;
            tv.tv_sec = 0;
            tv.tv_usec = 0;
            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 sockfd non risponde\n");
            close(sockfd_server);
            sockfd_server = -1;
            return false;
        }
    
        /* repeat not blocking connect() to check if it gives error saying connection already happened (from my teacher's slides) */
        conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
        if (conn_res == -1)  {
            if(errno == EISCONN)
                printf ("connect(): connection already existing, OK\n");
            else  {
                printf("connect(): connection failed\n");
                close(sockfd_server);
                sockfd_server = -1;
                return false;
            }
        }
        printf("connection OK\n");
    } else {
        debug_green("connected immediately\n");
    }
    if (!setBlocking(sockfd_server)) {
        perror("FCNTL:");
    }
    debug_green("CONNECT set socket back to blocking mode\n");fflush(stdout);
    // #define CONNECT_200_OK HTTP/1.1 200 Connection established\r\n\r\n
    std::string connect_200_to_client(CONNECT_200_OK);
    int connect_200_to_client_send = send(new_sockfd_client, connect_200_to_client.c_str(), sizeof(connect_200_to_client), 0);
    if (connect_200_to_client_send <= 0) {  
        perror("CONNECT send() 200 OK to client failed");
        close(sockfd_server);
        sockfd_server = -1;
        return false;
    }
    // finally calling proxyHTTPS
    hm.proxyHTTPS(new_sockfd_client, sockfd_server);
    
  9. 现在我应该准备好在从连接的两个方面之一获得数据时从客户端到服务器以及从服务器到客户端转发数据,但它永远不会成功。

    这里是应该从连接两端转发数据的代码:

    void proxyHTTPS(int new_sockfd_client, int sockfd_server) {
    
    printf("starting proxyHTTPS\n");
    fd_set fdset;
    int maxp1 = sockfd_server > new_sockfd_client ? sockfd_server+1 : new_sockfd_client+1;
    int r;
    int read_from_client = 0;
    int read_from_server = 0;
    int send_to_client = 0;
    int send_to_server = 0;
    struct timeval timeout;
    char https_buf[4096];
    int https_buf_size = sizeof(https_buf);
    memset(https_buf, 0, https_buf_size);
    // tried 0, 5, 10, 20, 30, 60 seconds timeout,
    // still got this problem
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;
    
    while (true) {
        FD_ZERO(&fdset);
        FD_SET(new_sockfd_client, &fdset);
        FD_SET(sockfd_server, &fdset);
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
    
        r = select(maxp1, &fdset, NULL, NULL, &timeout);
    
        if (r < 0) {
            perror("select()");
            break;
        }
    
        if (r == 0) { // select timed out
            debug_yellow("proxyHTTPS: select() request timeout 408\n");
            break;
        }
    
        if ((!FD_ISSET(new_sockfd_client, &fdset)) && ((!FD_ISSET(sockfd_server, &fdset)))) {
            debug_red("proxyHTTPS: SELECT sockfds not responding\n");
            break;
        }
    
        else if (FD_ISSET(new_sockfd_client, &fdset)) {
            debug_green("proxyHTTPS: reading from client and sending to server\n");
            read_from_client = recv(new_sockfd_client, https_buf, https_buf_size, 0);
            if (read_from_client < 0) {
                debug_red("proxyHTTPS recv() from client " << strerror(errno) << '\n');
                break;
            }
            else if (read_from_client == 0) {
                debug_yellow("proxyHTTPS client closed conn\n");
                break;
            }
            else { // read_from_client > 0
                send_to_server = send(sockfd_server, https_buf, read_from_client, 0);
                if (send_to_server > 0) {
                    debug_green("proxyHTTPS: sent to server " << std::to_string(send_to_server) << " bytes \n");
                }
                else if (send_to_server == 0) {
                    debug_yellow("proxyHTTPS: sent to server 0 bytes...\n");
                    break;
                }
                else {
                    debug_red("proxyHTTPS send() to server " << strerror(errno) << '\n');
                    break;
                }
            }
        }
        else if (FD_ISSET(sockfd_server, &fdset)) {
            debug_green("proxyHTTPS: reading from server and sending to client\n");
            read_from_server = recv(sockfd_server, https_buf, https_buf_size, 0);
            if (read_from_server < 0) {
                debug_red("proxyHTTPS recv() from server " << strerror(errno) << '\n');
                break;
            }
            else if (read_from_server == 0) {
                debug_yellow("proxyHTTPS server closed conn\n");
                break;
            }
            else { // read_from_server > 0
                send_to_client = send(new_sockfd_client, https_buf, read_from_server, 0);       
                if (send_to_client > 0) {
                    debug_green("proxyHTTPS: sent to client " << std::to_string(send_to_client) << " bytes\n");
                }
                else if (send_to_client == 0) {
                    debug_yellow("proxyHTTPS: sent 0 bytes to client, errno: " << errno << '\n');
                    break;
                }
                else {
                    debug_red("proxyHTTPS send() to client " << strerror(errno) << '\n');
                    break;
                }
            }
        }
    }
    debug_yellow("proxyHTTPS: quitting\n");
    }
    

    这是代理从客户端获取的CONNECT请求:

    CONNECT www.youtube.com:443 HTTP/1.1
    Host: www.youtube.com:443
    Proxy-Connection: keep-alive
    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36
    /r/n
    

    如果客户端发送CONNECT www.netflix.com:443 HTTP/1.1,我的代理将失败并在终端上打印:

    setting non block socket
    connected immediately
    setting block socket
    54.228.227.144:443 OK
    CONNECT: send to client HTTP/1.1 200 Connection established
    starting proxyHTTPS
    proxyHTTPS: reading from server and sending to client
    proxyHTTPS read from server: Connection refused
    quitting proxyHTTPS
    

    如果客户端发送CONNECT www.youtube.com:443 HTTP/1.1,我的代理会再次失败并显示:

    setting non block socket
    connected immediately
    setting block socket
    216.58.201.238:443 OK
    CONNECT: send to client HTTP/1.1 200 Connection established
    starting proxyHTTPS
    select(): Operation now in progress
    proxyHTTPS: select() request timeout 408
    quitting proxyHTTPS
    

    我想我已经完成了为了在客户端和服务器之间建立隧道所做的所有步骤,connect()总是正常的,所以错误必须在我的proxyHTTPS中。我不知道了!

    编辑1 :当perror()返回0时删除select(),因为errno未引发;改进了select()循环中的错误检查,将返回值>==区分开来。 尽管如此,仍然遇到同样的问题:在某种程度上,无法从客户端或服务器读取任何数据。

    编辑2 :更新了有关连接远程服务器的代码。

    我开始认为,由于select()循环似乎正确,问题可能是由其他原因造成的......

    上面使用的调试函数定义如下:

    #define DEFAULTCOLOR    "\033[0m"
    #define RED             "\033[22;31m"
    #define YELLOW          "\033[1;33m"
    #define GREEN           "\033[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);
    

0 个答案:

没有答案