SSL_read () не возвращается после успешного рукопожатия - PullRequest
0 голосов
/ 03 октября 2019

Я создал сервер и клиента, и они общаются в безопасном канале TLSv1.3, OpenSSL 3.0.0. Они должны иметь возможность отправлять и получать, но в этом тесте сервер только получает, а клиент только периодически отправляет данные приложения.

После успешного установления связи клиенты select() сигнализируют о наличии данных, доступных для чтения. Когда я пытаюсь, SSL_read() не возвращается. Я проверил сетевой трафик в wireshark и отладил библиотеку OpenSSL, чтобы выяснить, что 2 SESSION_TICKET отправляются после рукопожатия сервером клиенту, и это сообщение SSL_read() не обрабатывается. Просто для любопытства я отправил сообщение с сервером сразу после рукопожатия, чтобы посмотреть, как клиент реагирует на него. Удивительно, но клиент вышел из заблокированного состояния, и после этого связь работала нормально.

Из-за наблюдаемого поведения мое единственное предположение состоит в том, что я пытаюсь прочитать данные, предназначенные для OpenSSL, которые, вероятно, обрабатываются его конечным автоматом, когда я вызываю SSL_read(). Когда библиотека вызывает read(), на самом деле данные приложения недоступны, поэтому она блокирует поток.

Функцию-член, которую я использую для чтения байтов "count", можно увидеть ниже.

int EventHandler::readByte(CommBuffer &buffer, ssize_t count, struct timeval &tmout){
  fd_set rfds;
  int retSelect, retRead;
  int readByte=0;
  int endRead=0;
  int readSize=count;

  buffer.setBufferSize(count+1);
  do{
    FD_ZERO(&rfds);
    FD_SET(fd,&rfds);
    retSelect=select(fd+1,&rfds,NULL,NULL,&tmout);
    if(retSelect>0){
      if (encryptionOn) {
        retRead = SSL_read(ssl, (const_cast<char*>(&buffer.get()[readByte])), readSize); // Thread blocks here.
      } else {
        retRead = read(fd, (const_cast<char*>(&buffer.get()[readByte])), readSize);
      }
      if( retRead > 0 ){
        readByte+=retRead;
        allReadBytes+=retRead;
        buffer.setDataSize(readByte);
        if(readByte>=count) endRead=1;
        readSize=count-readByte;
      }
      else{
        if( retRead == -1 && errno == EINTR  ){
          continue;
        }
        if(fd>=0){
          close(fd);
        }
        fd=CLOSED_FD;
        return (CLOSED_FD);
      }
    }
    else if(retSelect==0){
      // handle timeout error
      return(TIMEOUT_FD);
    }
    else{
      return(retSelect);
    }
  }while(endRead!=1);
  return(readByte);
} 

Обратите внимание, что эта функция прекрасно работает без шифрования. (read() вместо SSL_read())

  • Что я делаю не так?
  • Как читать данные?
  • Как бы вы изменили эту функцию?

1 Ответ

1 голос
/ 03 октября 2019

select работает на уровне TCP, а SSL_read работает на уровне TLS. SSL_read в блокирующем сокете (обычно) будет возвращаться только при наличии данных приложения, в то время как select будет сигнализировать, когда в сокете доступны данные любого типа, т. Е. Также для записей, не относящихся к приложению, или неполных кадров SSL.

В вашем конкретном случае эти данные являются билетами сеанса, которые с TLS 1.3 больше не отправляются как часть рукопожатия TLS, а после рукопожатия. Но с более низкими версиями TLS может также случиться так, что SSL_read заблокирован, даже если select вернет, что данные доступны: данные приложения в TLS отправляются в рамках SSL, и может случиться, что select сообщит о доступных данных, хотя только частьфрейма SSL имеется. В этом случае SSL_read будет блокироваться, пока не будет прочитан полный кадр SSL. Кроме того, SSL_read будет блокировать повторное согласование SSL, даже если select будет сигнализировать о доступных данных.

Кроме того, может случиться, что select не будет сигнализировать о доступных данных, но SSL_read вернет некоторые. Это тот случай, когда предыдущий SSL_read не использовал все данные, которые были включены в последние кадры SSL, уже прочитанные из сокета TCP.

Другими словами: select не следует использовать с блокирующими сокетамив SSL. Вместо этого сокеты должны быть неблокирующими, чтобы SSL_read (и другие функции) могли выходить из строя с SSL_ERROR_WANT_READ или SSL_ERROR_WANT_WRITE, которые следует обрабатывать в соответствии с документацией. Кроме того, SSL_pending должен вызываться для проверки данных, которые уже были прочитаны из сокета TCP, но которые еще не были использованы SSL_read.

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