【问题标题】:SSL_accept with blocking socket带有阻塞套接字的 SSL_accept
【发布时间】:2009-11-16 20:02:52
【问题描述】:

我用 SSL 和 阻塞 套接字制作了一个服务器。 当我使用 telnet 连接时(因此它不进行握手),SSL_accept 会无限期阻塞并阻塞每个新的握手/接受(以及定义为新连接)。

我该如何解决这个可怕的问题?

【问题讨论】:

    标签: c++ ssl tcp


    【解决方案1】:

    为什么不在调用SSL_accept() 之前将套接字流设置为非阻塞模式,然后在SSL_accept() 返回SSL_ERROR_WANT_READ 或SSL_ERROR_WANT_WRITE 时阻塞select() 之类的东西?或者,您可以在调用 SSL_accept() 之前阻止 select()。要么应该工作。这样,您至少可以限制连接因DoS 之类的行为/攻击而被阻止的时间。

    请记住,SSL/TLS 是面向记录的,这意味着您必须循环直到读取完整记录。 SSL_pending() 可以在这种情况下提供帮助。

    【讨论】:

      【解决方案2】:

      您可以将套接字置于非阻塞模式,然后您会从 SSL_accept 获得案例 SSL_ERROR_WANT_READ 或 SSL_ERROR_WANT_WRITE。然后您可以睡一会,然后再次尝试 SSL_accept。在某个超时值之后,您可以退出并关闭 ssl 和套接字句柄。

      请注意,这将影响所有 SSL 操作,这意味着您需要为所有读/写/关闭调用执行类似的循环。基本上,任何可以返回 WANT_READ 或 WANT_WRITE 的调用。

      如果您不喜欢轮询的想法,您可以使用 select 来确定套接字上是否有可用数据……但这可能会有点复杂。

      您还可以尝试在 SSL_accept 循环之后将套接字重新设置为阻塞模式,然后继续您的应用程序。

      【讨论】:

        【解决方案3】:

        我认为下面的代码可能会帮助其他人解决问题。它没有经过充分测试,以它为灵感。

          //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);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-31
          • 2013-10-15
          • 2011-03-22
          • 1970-01-01
          • 1970-01-01
          • 2011-08-19
          • 2021-03-03
          • 1970-01-01
          相关资源
          最近更新 更多