我用SSL做了一个服务器,并且用阻塞了套接字。 当我连接到telnet(所以它不会握手)时,SSL_accept将无限期地阻塞,并阻止每个新的握手/接受(并按定义新连接)。SSL_accept with blocking socket
我该如何解决这个可怕的问题?
我用SSL做了一个服务器,并且用阻塞了套接字。 当我连接到telnet(所以它不会握手)时,SSL_accept将无限期地阻塞,并阻止每个新的握手/接受(并按定义新连接)。SSL_accept with blocking socket
我该如何解决这个可怕的问题?
为什么不直接调用SSL_accept()之前设置流套接字到非阻塞模式,然后用超时上像select()阻止如果SSL_accept()返回SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE?或者,您可以在调用SSL_accept()之前阻塞select()。要么应该工作。这样,您至少可以限制连接因类似行为/攻击而被阻止的时间。
请记住,SSL/TLS是面向记录的,这意味着您必须循环直到读取完整记录。 SSL_pending()可以帮助在这种情况下。
请勿使用telnet连接?
通常情况下,可以使用TLS或纯文本连接的服务通过以明文形式建立连接起作用,然后提供命令以请求“升级”连接以使用TLS。扩展SMTP和“STARTTLS”命令就是一个例子。
没有这种命令的服务通常对TLS和非TLS流量使用不同的端口。例如,端口80上的HTTP和端口443上的HTTPS。到端口443的明文连接不起作用。
你想看什么样的行为?
您可以将套接字置于非阻塞模式,然后您将从SSL_accept中获得SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE的情况。然后你可以睡一会儿,然后再次尝试SSL_accept。在超时值后,您可以退出并关闭ssl和套接字句柄。
请注意,这将影响所有SSL操作,这意味着您需要为所有读取/写入/关闭调用执行类似的循环。基本上,任何可以返回WANT_READ或WANT_WRITE的调用。
如果您不喜欢轮询的想法,您可以使用select来判断您的套接字上是否有可用的数据......但这可能会有点复杂。
您也可以尝试在SSL_accept循环之后将套接字置回阻塞模式,并继续使用您的应用程序。
我认为下面的代码可能有助于他人解决问题。它没有经过充分测试,以此为灵感。
//Nonblocking SSL accept based on ACE/ace/SSL/SSL_SOCK_Acceptor.cpp
SSL_CTX* ctx;
ctx = initServerCTX(); // initialize SSL
loadCertificates(ctx, certificate, privateKey); // load certs
...
SSL* ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, fd); /* set connection socket to SSL state */
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
{
printf("fcntl: F_GETFL \n");
return false;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
{
printf("fcntl: F_SETFL \n");
return false;
}
int status = -1;
struct timeval tv, tvRestore;
tv.tv_sec = 2;
tv.tv_usec = 0;
tvRestore = tv;
fd_set writeFdSet;
fd_set readFdSet;
do
{
tv = tvRestore;
FD_ZERO(&writeFdSet);
FD_ZERO(&readFdSet);
status = ::SSL_accept(ssl);
switch (::SSL_get_error(ssl, status))
{
case SSL_ERROR_NONE:
status = 0; // To tell caller about success
break; // Done
case SSL_ERROR_WANT_WRITE:
FD_SET(fd, &writeFdSet);
status = 1; // Wait for more activity
break;
case SSL_ERROR_WANT_READ:
FD_SET(fd, &readFdSet);
status = 1; // Wait for more activity
break;
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SYSCALL:
// The peer has notified us that it is shutting down via
// the SSL "close_notify" message so we need to
// shutdown, too.
printf("Peer closed connection during SSL handshake,status:%d", status);
status = -1;
break;
default:
printf("Unexpected error during SSL handshake,status:%d", status);
status = -1;
break;
}
if (status == 1)
{
// Must have at least one handle to wait for at this point.
status = select(fd + 1, &readFdSet, &writeFdSet, NULL, &tv);
// 0 is timeout, so we're done.
// -1 is error, so we're done.
// Could be both handles set (same handle in both masks) so
// set to 1.
if (status >= 1)
{
status = 1;
}
else // Timeout or failure
{
printf("SSL handshake - peer timeout or failure");
status = -1;
}
}
}
while (status == 1 && !SSL_is_init_finished(ssl));
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
{
printf("fcntl: F_GETFL \n");
return false;
}
if (fcntl(fd, F_SETFL, flags & (~O_NONBLOCK)) < 0)
{
printf("fcntl: F_SETFL \n");
return false;
}
return (status >= 0);
是的,这就是我正在做的事情,但想象一下客户端在断开握手时断开连接,它会阻止其他握手。 我的意思是: - 在TCP接受主线程 完成 - 在SSL_accept由每个新线程 做 - 如果一个握手(在一个单独的线程)的块,它会阻止每一个握手 我想行为看到的不是阻止每一次新的握手 – fedj 2009-11-16 20:12:50