C++ 如何通过透明代理实现SSL隧道?

C++ 如何通过透明代理实现SSL隧道?,c++,networking,ssl,winsock,tunnel,C++,Networking,Ssl,Winsock,Tunnel,我已经阅读并设置了一个HTTP透明代理,它可以完美地处理未加密的流量 阅读了以上内容,并在谷歌上搜索了我的大脑,为通过代理的安全流量创建隧道的公认方法似乎是: 连接到请求的主机,然后让代理将“HTTP 200…”确认消息发送回客户端,然后从此点开始,只需在客户端和服务器之间传递所有进一步的数据通信 但是,当我尝试此操作时,客户端(Chrome浏览器)会用三个wingdings字符响应“HTTP 200…”消息,我将这些字符转发给远程主机。此时没有响应,连接失败 以下是我在连接到主机后使用的代码:

我已经阅读并设置了一个HTTP透明代理,它可以完美地处理未加密的流量

阅读了以上内容,并在谷歌上搜索了我的大脑,为通过代理的安全流量创建隧道的公认方法似乎是:

连接到请求的主机,然后让代理将“HTTP 200…”确认消息发送回客户端,然后从此点开始,只需在客户端和服务器之间传递所有进一步的数据通信

但是,当我尝试此操作时,客户端(Chrome浏览器)会用三个wingdings字符响应“HTTP 200…”消息,我将这些字符转发给远程主机。此时没有响应,连接失败

以下是我在连接到主机后使用的代码:

if((*request=='C')&&(*(request+1)=='O')&&(*(request+2)=='N')&&(*(request+3)=='N'))
{

    int recvLen;

    send(output,htok,strlen(htok),0); //htok looks like "HTTP/1.0 200 Connection Established\nProxy-Agent: this_proxy\r\n\r\n"

    std::memset(buff,0,bSize);
    int total;
        int bytes;
        int n;
        char cdata[MAXDATA];
        while ((recvLen = recv(output, buff, bSize-1,0)) > 0) //recving from client - here we get wingdings
        {
            memset(cdata,0, MAXDATA);
            strcat(cdata, buff);
            while(recvLen>=bSize-1)//just in case buff is too small
            {
                std::memset(buff,0,bSize);
                recvLen=recv(output,buff,bSize-1,0);
                strcat(cdata, buff);
            }

            total = 0;
            bytes = strlen(cdata);                      
            cout << cdata << endl;//how I see the wingdings
            while (total < strlen(cdata))
            {       
                n = send(requestSock, cdata + total, bytes,0);//forwarding to remote host
                if(n == SOCKET_ERROR)
                {
                    cout << "secure sending error" << endl;
                    break;
                }
                total += n;
                bytes -= n;
            }

            std::memset(buff,0,bSize);
            recvLen=recv(requestSock, buff, bSize,0);//get reply from remote host
            if (recvLen > 0)
            {
                do
                {
                    cout<<"Thread "<<threadid<<" [Connection:Secure]: "<<recvLen<<endl;


                    send(output, buff, recvLen,0);//forward all to client

                    recvLen= recv(requestSock, buff, bSize,0);

                    if(0==recvLen || SOCKET_ERROR==recvLen)         
                    {
                        cout<<"finished secure receiving or socket error"<<endl;
                        break;
                    }

                }while(true);
            }
                      }//end while, loop checks again for client data
if((*request='C')&&(*(request+1)='O')&(*(request+2)='N')&(*(request+3)='N'))
{
int recvLen;
发送(输出,htok,strlen(htok),0);//htok看起来像“HTTP/1.0 200已建立连接\n代理程序:此代理程序\r\n\r\n”
标准::memset(buff,0,bSize);
整数合计;
整数字节;
int n;
字符cdata[MAXDATA];
while((recvLen=recv(output,buff,bSize-1,0))>0)//从客户端接收-这里我们得到wingdings
{
memset(cdata、0、MAXDATA);
strcat(cdata,buff);
while(recvLen>=bSize-1)//以防buff太小
{
标准::memset(buff,0,bSize);
recvLen=recv(输出,增益,bSize-1,0);
strcat(cdata,buff);
}
总数=0;
字节=strlen(cdata);

cout我认为您不应该假设流量不包含ASCII
NUL
字符:

            strcat(cdata, buff);
        }

        total = 0;
        bytes = strlen(cdata);

如果流中有ASCII
NUL
s,则这些操作将失败。

您的代码比需要的复杂得多。只需读入字符数组,保存返回的长度,并在循环中写入同一数组中的大量字节,直到recv()返回0.4行代码,其中包括两行大括号。不要尝试组装整个传入消息,只需在消息传入时中继任何消息。否则,您只会增加延迟和编程错误。请删除所有strXXX()总共调用。

这一点很好。我使用std::string来节省一些周期,但网络操作可能会使其无效。
std::string
处理
0x00
字节的能力也很差。二进制数据是二进制的,不能使用字符串类来处理。EJP的回答建议了一种比我更好的方法——不要尝试解释数据。最初我是这样做的,但后来我想看看什么样的数据是在什么都没有发生的情况下从客户端来的。客户端“hello”应该包含3个以上的wingding字符…@PhilipLangford将二进制数据视为ASCII不会告诉你任何有用的信息。同样,称它们为“wingdings”也是徒劳的e、 字节的实际值是多少?我采纳了您的建议,简化了传输,现在可以看到字节交换,但是,我的逻辑导致远程主机无法满足其请求的无限循环。Psuedo代码:'while(recv from client>0){send to host;while(recv from host>0)send to client;}
recv()
send()
如果连接已断开,则返回0。您需要检查两个循环的返回值是否都小于1,以便中断循环。您也不应该运行两个单独的循环。使用调用
select()的单个循环
使用两个套接字来确定何时可以读取其中一个连接。当其中一个连接可读时,使用
ioctlsocket()
来确定有多少字节可用,
recv()
就那么多字节,
send()
将字节发送到另一个连接,然后返回到
select()
。通过这种方式,两个连接可以同时向对方发送数据。确切地说,还有一个微妙之处,我一直在讲。当您从recv()获得零时,请关闭对的另一个套接字上的输出(即传输EOS)并且,当和仅当您已经关闭了从零开始的套接字的输出时,关闭两个套接字。否则,您就有过早关闭的危险:考虑传入的关闭可能只是关机本身,并且可能还有其他数据流。调用可以发送少于recvLen的数据,而您忽略了这一事实。另一个问题是将SSL中的二进制数据视为文本(您的代码可以用于文本HTTP请求,但无论如何都会使用二进制数据失败)。