以下代码部分的目的是在套接字fd-set上进行轮询,如果数据(ssl已加密)可用,请读取它并通过openssl库对其进行解密。位于底层的传输层是TCP Stream,因此数据以流(而不是数据包)形式出现。
现在,如果从对等方快速连续发送多个数据包(假设2个长度为85字节的数据包),那么tcp receive将返回同一缓冲区中的数据包,并且接收的字节数为170.因此,我们有一个缓冲区,它携带2个ssl加密数据包(或n个数据包)。对于ssl解密,我们需要调用BIO_write()将缓冲区写入ssl_bio,然后调用ssl_read()来检索dycrypted缓冲区。但是,尽管BIO_write()正在将170个字节写入bio,但似乎ssl_read()只返回一个dycrypted数据包(43个字节)。没有返回错误。如何知道bio中是否还有未处理的字节。是否有任何出路或代码中是否有任何错误?
在tcp recv()中接收到单个数据包时,代码工作正常。
int iReadyFds = poll( PollFdSet, iFdCount, iTimeout);
for(iFdIndx = 0; iFdIndx < (iFdCount) && (iReadyFds>0); ++iFdIndx)
{
if((PollFdSet[iFdIndx].events == 0) ||
(PollFdSet[iFdIndx].fd == 0) ||
(PollFdSet[iFdIndx].revents != POLLIN)
)
{
continue;
}
/* we have data to read */
int iMsgLen = 0;
int iFd = PollFdSet[iFdIndx].fd;
/*This is TCP Receive. Returns 170 bytes*/
iRcvdBytes = recv( iSocketId, ( void* )pcInBuffer, PN_TCP_STREAM_MAX_RX_BUFF_SIZE, 0 );
/*Writing into SSL BIO, this will be retrieved by ssl_read*/
/*iNoOFBytes = 170*/
iNoOFBytes = BIO_write(m_pRead_bio, pcInBuffer, iRcvdBytes);
if(iNoOFBytes <= 0)
{
printf("Error");
return -1;
}
char* pcDecodedBuff = (char*)malloc(1024);
/*here it returns 43 bytes of decrypted buffer(1 packet). the other packet vanishes*/
iReadData = SSL_read(m_psSSL, pcDecodedBuff, 1024);
if ((iReadData == -1) || (iReadData == 0))
{
error = SSL_get_error(psPskTls->m_psSSL, iReadData);
if(error == SSL_ERROR_ZERO_RETURN
|| error == SSL_ERROR_NONE
|| error == SSL_ERROR_WANT_READ)
{
printf("Error");
}
}
iReadyFds--;
}
答案 0 :(得分:4)
OpenSSL通常只会一次读取和解密一条记录。再次调用SSL_read将为您提供下一条记录。如果您不知道是否有其他记录需要阅读,您可以询问基础传输,如果它当前是“可读的”#34; - 或者只是调用SSL_read()并处理错误(如果使用非阻塞IO)。
在某些情况下(例如,如果您使用&#34; read_ahead&#34;功能),OpenSSL可能会在内部缓冲一些数据。您可以使用SSL_has_pending()(对于OpenSSL 1.1.0)或SSL_pending()(对于OpenSSL 1.0.2+)查看是否存在缓冲的内部数据。请参阅https://www.openssl.org/docs/man1.1.0/ssl/SSL_has_pending.html。
答案 1 :(得分:2)
经过几次实验并阅读openssl文档后,我已经能够解决这个问题了。据我所知,在相当高的速度下(每秒ssl连接超过1000个应用程序数据事务),在使用异步openssl实现的情况下肯定会出现这个问题。
根据openssl实施 -
得出答案由于这个问题与ssl_read()有关,我们将从上面的第二点继续讨论。据我所知,ssl_read()实际上执行openssl的内部fsm,它一次从read_bio读取和处理缓冲区。缓冲区的其余部分(如果有的话)作为未处理的缓冲区驻留在读取生物中。因此,ssl_pending()永远不会返回任何内容,因为没有剩余的处理缓冲区可供读取。找出是否需要处理和读取bio的唯一方法是进行连续的ssl_read()调用,直到它返回0字节为止。
修改后的代码看起来应该是这样的 -
int iReadyFds = poll( PollFdSet, iFdCount, iTimeout);
for(iFdIndx = 0; iFdIndx < (iFdCount) && (iReadyFds>0); ++iFdIndx)
{
if((PollFdSet[iFdIndx].events == 0) ||
(PollFdSet[iFdIndx].fd == 0) ||
(PollFdSet[iFdIndx].revents != POLLIN)
)
{
continue;
}
/* we have data to read */
int iMsgLen = 0;
int iFd = PollFdSet[iFdIndx].fd;
/*This is TCP Receive. Returns 170 bytes*/
iRcvdBytes = recv( iSocketId, ( void* )pcInBuffer, PN_TCP_STREAM_MAX_RX_BUFF_SIZE, 0 );
/*Writing into SSL BIO, this will be retrieved by ssl_read*/
/*iNoOFBytes = 170*/
iNoOFBytes = BIO_write(m_pRead_bio, pcInBuffer, iRcvdBytes);
if(iNoOFBytes <= 0)
{
printf("Error");
return -1;
}
char* pcDecodedBuff = (char*)malloc(1024);
/*here it returns 43 bytes of decrypted buffer(1 packet).
So we keep on reading until all the packets are processed and read*/
while(iReadData = SSL_read(m_psSSL, pcDecodedBuff, 1024) > 0)
{
doSomething(pcDecodedBuff, iReadData);**
}
if ((iReadData == -1) || (iReadData == 0))
{
error = SSL_get_error(psPskTls->m_psSSL, iReadData);
if(error == SSL_ERROR_ZERO_RETURN
|| error == SSL_ERROR_NONE
|| error == SSL_ERROR_WANT_READ)
{
printf("Error");
}
}
iReadyFds--;
}
检查如何使用连续的ssl_read()来确保read_BIO中没有剩余未处理的数据。
while(iReadData = SSL_read(m_psSSL,pcDecodedBuff,1024)&gt; 0) { doSomething(pcDecodedBuff,iReadData); }
使用此代码,我遇到的问题得到了解决。希望它也能帮助别人。
答案 2 :(得分:2)
我也在使用内存BIO将SSL与非阻塞套接字一起使用(使用poll)。我最终得到的代码看起来与您的解决方案类似,但还有一个额外的步骤;在调用SSL_get_error
之后,您需要检查SSL是否已请求写入操作。 SSL_read
可能导致SSL对象需要执行套接字写入;如果对等方请求重新协商,就会发生这种情况。
以下是ssl_server_nonblock.c完整代码的摘要...其中的部分执行了BIO_write
和SSL_read
/* Process SSL bytes received from the peer. The data needs to be fed into the
SSL object to be unencrypted. On success returns 0, on SSL error -1. */
int on_read_cb(char* src, size_t len) {
char buf[DEFAULT_BUF_SIZE]; /* used for copying bytes out of SSL/BIO */
enum sslstatus status;
int n;
while (len > 0) {
n = BIO_write(client.rbio, src, len);
if (n<=0)
return -1; /* if BIO write fails, assume unrecoverable */
src += n;
len -= n;
if (!SSL_is_init_finished(client.ssl)) {
n = SSL_accept(client.ssl);
status = get_sslstatus(client.ssl, n);
/* Did SSL request to write bytes? */
if (status == SSLSTATUS_WANT_IO)
do {
n = BIO_read(client.wbio, buf, sizeof(buf));
if (n > 0)
queue_encrypted_bytes(buf, n);
else if (!BIO_should_retry(client.wbio))
return -1;
} while (n>0);
if (status == SSLSTATUS_FAIL)
return -1;
if (!SSL_is_init_finished(client.ssl))
return 0;
}
/* The encrypted data is now in the input bio so now we can perform actual
* read of unencrypted data. */
do {
n = SSL_read(client.ssl, buf, sizeof(buf));
if (n > 0)
client.do_something(buf, (size_t)n);
} while (n > 0);
status = get_sslstatus(client.ssl, n);
/* Did SSL request to write bytes? This can happen if peer has requested SSL
* renegotiation. */
if (status == SSLSTATUS_WANT_IO)
do {
n = BIO_read(client.wbio, buf, sizeof(buf));
if (n > 0)
queue_encrypted_bytes(buf, n);
else if (!BIO_should_retry(client.wbio))
return -1;
} while (n>0);
if (status == SSLSTATUS_FAIL)
return -1;
}