SSL_accept с блокирующим сокетом - PullRequest
1 голос
/ 16 ноября 2009

Я сделал сервер с SSL и , блокирующий сокеты. Когда я соединяюсь с telnet (так что он не выполняет рукопожатие), SSL_accept блокируется на неопределенный срок и блокирует каждое новое рукопожатие / принятие (и по определению новые соединения).

Как я могу решить эту ужасную проблему?

Ответы [ 4 ]

2 голосов
/ 17 ноября 2009

Почему бы просто не установить поток сокетов в неблокирующий режим перед вызовом SSL_accept () , а затем заблокировать что-то вроде select () с таймаутом, если SSL_accept () вернет SSL_ERROR_WANT_READ или SSL_ERROR_WANT_WRITE? Кроме того, вы можете заблокировать select () перед вызовом SSL_accept (). Либо должно работать. Таким образом, вы можете по крайней мере ограничить время, в течение которого соединение блокируется из-за поведения / атаки типа DoS .

Имейте в виду, что SSL / TLS ориентирован на запись, что означает, что вы должны зацикливаться, пока полная запись не будет прочитана. SSL_pending () может помочь в таких случаях.

1 голос
/ 16 ноября 2009

Не соединяться с telnet?

Обычно служба, которая может использовать либо TLS, либо соединения в виде открытого текста, работает, устанавливая соединение в виде открытого текста, а затем предлагает команду для запроса «обновления» соединения для использования TLS. Расширенный SMTP и команда «STARTTLS» являются примером этого.

Сервисы без такой команды обычно используют разные порты для трафика TLS и не-TLS. Например, HTTP на порту 80 и HTTPS на порту 443. Соединение в незашифрованном виде с портом 443. не будет работать.

Какое поведение вы хотели бы видеть?

0 голосов
/ 03 мая 2016

Я думаю, что приведенный ниже код может помочь другим решить проблему. Это не полностью проверено, принимайте это как вдохновение.

  //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);
0 голосов
/ 17 ноября 2009

Вы можете перевести сокет в неблокирующий режим, тогда вы получите кейс SSL_ERROR_WANT_READ или SSL_ERROR_WANT_WRITE из SSL_accept. Затем вы можете немного поспать и снова попробовать SSL_accept. Через некоторое время ожидания вы можете выйти и закрыть дескрипторы ssl и socket.

Обратите внимание, что это повлияет на все операции SSL, а это значит, что вам нужно будет сделать подобный цикл для всех ваших вызовов чтения / записи / выключения. По сути, любой вызов, который может вернуть WANT_READ или WANT_WRITE.

Если вам не нравится идея опроса, вы можете использовать select, чтобы выяснить, есть ли у вас данные в сокете ... но это может немного усложнить.

Вы также можете попробовать перевести сокет в режим блокировки после цикла SSL_accept и продолжить работу с приложением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...