iocp openssl 对等服务器在与 ConnectEx 连接后关闭连接

Posted

技术标签:

【中文标题】iocp openssl 对等服务器在与 ConnectEx 连接后关闭连接【英文标题】:iocp openssl peer server closes the connection after connecting with ConnectEx 【发布时间】:2019-11-29 16:00:09 【问题描述】:

我在使 openssl 在 windows 上使用 iocp 时遇到问题,目前仅尝试客户端模式

我可以使用内存 bios 进行异步写入和读取,但我很难让异步连接和握手工作

如果我使用 SSL_connect 进行连接,然后使用 SSL_do_handshake 进行握手,然后将 bios 替换为我可以很好地异步读写的内存 bios

在异步连接中,我执行以下操作:

发出与 ConnectEx 的连接 使用 iocp 处理结果 调用 SSL_do_handshake 并检索结果 检查 SSL_WANT_READ 或 SSL_WANT_WRITE 是否有错误,我总是得到前者 然后我尝试将一些数据接收到 in bio(尝试异步和同步)但这里失败。我尝试使用 recv 并完成握手同步,但 recv 返回 0 。也尝试过 WSARecv,但在完成通知中我传输了 0 个字节,所以我结束了连接

这是我正在使用的一些相关代码:

class AsyncSSLSocket;
using SSLOnConnect = std::function<void(AsyncSSLSocket&, std::error_code, SockAddr&)>;

struct SSLConnectCtx : public io::BaseIoCtx

    AsyncSSLSocket *sslsock;
    std::variant<IpV4Addr, IpV6Addr> addr;
    SSLOnConnect OnConnect;

    virtual void HandleIocpPacket(io::iocp::CompletionResult& IocpPacket) override;

    void CompleteHandShake();

;

inline void SSLConnectCtx::HandleIocpPacket(io::iocp::CompletionResult& IocpPacket)


    DWORD Transferred, RecvFlags;
    WSAGetOverlappedResult(sock->GetHandle().get(), reinterpret_cast<LPWSAOVERLAPPED>(IocpPacket.Ctx), &Transferred, 0, &RecvFlags);
    auto error = sock->LastError(); // WSAGetLastError()

    if (!error)
    
        IocpPacket.Ctx = nullptr;
        sslsock->sslhandle.SetClientMode(); // SSL_set_connect_state
        CompleteHandShake();
        return;
    

    auto& peer_addr = ExtractAddr(addr);
    OnConnect(*sslsock, error, peer_addr);


inline void SSLConnectCtx::CompleteHandShake()

    auto& sslhandle = sslsock->sslhandle;
    int ret = sslhandle.DoHandShake(); // SSL_do_handshake
    std::error_code error;

    if (ret < 0)
    
        error = sslhandle.LastError(ret);

        if (sslhandle.WantRead(error.value())) // always get here
        
            error = sslsock->FillInBIO([this](auto&, auto error, uint32_t trans)
            
                MakeErrorIfZeroIsRecved(trans, error); // set error to std::errc::connection_abort (106) if WSARecv received 0 bytes
                if (!error)
                
                    CompleteHandShake();
                    return;
                
                // always get here 
                // prints : [!] failed to get data for the handshake !, error : generic:106 ==> connection aborted
                std::cout << "[!] failed to get data for the handshake !, error : " << error << " ==> " << error.message() << std::endl;
                OnConnect(*sslsock, error, ExtractAddr(addr));
                delete this;
            );
            if (!error)
                return;
        

        else if (sslhandle.WantWrite(error.value()))
        
            error = sslsock->FlushOutBIO([this](auto&, auto error, uint32_t) 
            
                if (!error)
                
                    CompleteHandShake();
                    return;
                
                OnConnect(*sslsock, error, ExtractAddr(addr));
                delete this;
            );
            if (!error)
                return;
        
    

    OnConnect(*sslsock, error, ExtractAddr(addr));
    delete this;


void AsyncSSLSocket::AsyncConnect(SockAddr& addr, const SSLOnConnect& OnConnect)
    
        SSLConnectCtx * ctx new SSLConnectCtx ;
        ctx->sslsock = this;
        ctx->sock = &sock;
        SetAddrs(ctx->addr, addr);
        ctx->OnConnect = OnConnect;

        auto result = sock.AsyncConnect(addr, *ctx);
        if (result)
            return;
        delete ctx;
        OnConnect(*this, sock.LastError(), addr);
    

    std::error_code AsyncSSLSocket::FillInBIO(SSLOnRead&& OnRead)
    
        char *buff = new char[1024];
        CachedReadBuff = io::IoBuffer(buff, 1024);
        CachedOnRead = std::move(OnRead);

        SockRecvCtx * ctx new SockRecvCtx ;
        ctx->sock = &sock;
        ctx->OnRecv = [this](Socket&, std::error_code error, uint32_t transferred)
        
            MakeErrorIfZeroIsRecved(transferred, error);
            if (!error)
                inBIO.Write(CachedReadBuff.data(), static_cast<int>(transferred));              
            delete CachedReadBuff.data();
            CachedOnRead(*this, error, transferred);
        ;

        auto result = sock.AsyncRecv(CachedReadBuff, *ctx);
        if (result)
            return std::error_code;
        delete ctx;
        return result.error;
    

【问题讨论】:

【参考方案1】:

似乎 SSL_do_handshake 将一些数据放入 out bio 并返回 SSL_get_error 返回 SSL_ERROR_WANT_READ 所以我在服务器等待从客户端接收一些数据时从服务器读取更多数据,因此 WSARecv 或 recv 等待几秒钟(也许服务器有超时?)直到服务器关闭连接并且我收到 0 个字节。

所以我使用了这段代码:

inline void SSLConnectCtx::CompleteHandShake()

    auto& sslhandle = sslsock->sslhandle;
    int ret = sslhandle.DoHandShake(); // SSL_do_handshake
    std::error_code error;

    if (ret < 0)
    

        int pending = sslsock->outBIO.Pending();
        if (pending > 0)
        
            std::cout << "[!!!] there is some data to send !" << std::endl;
            error = sslsock->FlushOutBIO([this](auto&, auto error, uint32_t)
            
                if (!error)
                
                    CompleteHandShake();
                    return;
                
                OnConnect(*sslsock, error, ExtractAddr(addr));
                delete this;
            );
            if (!error)
                return;
            std::cout << "[!] failed to flush ! , error " << error << " ==> " << error.message() << std::endl;
        

        error = sslhandle.LastError(ret);

        if (sslhandle.WantRead(error.value())) // always get here
        
            std::cout << "[!!!] needs to read in the bio" << std::endl;
            error = sslsock->FillInBIO([this](auto&, auto error, uint32_t trans)
            
                MakeErrorIfZeroIsRecved(trans, error); // set error to std::errc::connection_abort (106) if WSARecv received 0 bytes
                if (!error)
                
                    CompleteHandShake();
                    return;
                
                // always get here 
                // prints : [!] failed to get data for the handshake !, error : generic:106 ==> connection aborted
                std::cout << "[!] failed to get data for the handshake !, error : " << error << " ==> " << error.message() << std::endl;
                OnConnect(*sslsock, error, ExtractAddr(addr));
                delete this;
            );
            if (!error)
                return;
        

        else if (sslhandle.WantWrite(error.value()))
        
            error = sslsock->FlushOutBIO([this](auto&, auto error, uint32_t) 
            
                if (!error)
                
                    CompleteHandShake();
                    return;
                
                OnConnect(*sslsock, error, ExtractAddr(addr));
                delete this;
            );
            if (!error)
                return;
        
    

    OnConnect(*sslsock, error, ExtractAddr(addr));
    delete this;

但现在错误是 SSL_ERROR_WANT_READ 而需要先写入再读取!

我读到在通信过程中的任何时候都可能会重新协商,所以如果 SSL_write 返回 SSL_ERROR_WANT_READ 我应该开始读取或发送待处理的数据吗?如果 SSL_read 返回 SSL_ERROR_WANT_WRITE 我应该开始发送数据还是接收数据?

【讨论】:

以上是关于iocp openssl 对等服务器在与 ConnectEx 连接后关闭连接的主要内容,如果未能解决你的问题,请参考以下文章

一种 Windows IOCP 整合 OpenSSL 实现方案

一种 Windows IOCP 整合 OpenSSL 实现方案

一种 Windows IOCP 整合 OpenSSL 实现方案

一种 Windows IOCP 整合 OpenSSL 实现方案

将 SSL 添加到基于 IOCP 的 Windows 服务器的最简单方法是啥?

一只会铲史的猫:我开发的软件一览