Как обрабатывать OpenSSL SSL_ERROR_WANT_READ/WANT_WRITE на неблокирующих сокетах

Библиотека OpenSSL позволяет читать из базового сокета с SSL_read и записывать в него с помощью SSL_write. Эти функции могут возвращаться с использованием SSL_ERROR_WANT_READ или SSL_ERROR_WANT_WRITE в зависимости от требований протокола ssl (например, при перестройке соединения).

Я действительно не понимаю, что API хочет, чтобы я сделал с этими результатами.

Отображение серверного приложения, которое принимает клиентские соединения, устанавливает новый сеанс ssl, блокирует базовый сокет, а затем добавляет filedescriptor в цикл select/poll/epoll.

Если клиент отправляет данные, основной цикл отправляет его в ssl_read. Что нужно сделать здесь, если возвращается SSL_ERROR_WANT_READ или SSL_ERROR_WANT_WRITE? WANT_READ может быть легко, потому что следующая основная итерация цикла может привести только к другому ssl_read. Но если ssl_read возвращает WANT_WRITE, с какими параметрами он должен быть вызван? И почему библиотека не выдает сам вызов?

Если сервер хочет отправить клиенту некоторые данные, он будет использовать ssl_write. Опять же, что делать, если вернутся WANT_READ или WANT_WRITE? Можно ли ответить на WANT_WRITE, повторив тот же самый вызов, который был вызван? И если WANT_READ будет возвращено, нужно ли вернуться в основной цикл и позволить этому select/poll/epoll позаботиться об этом? Но как насчет сообщения, которое должно быть написано в первую очередь?

Или нужно ли читать чтение сразу после неудачной записи? Затем, что защищает от чтения байтов из протокола приложения и затем имеет дело с ним где-то на окраине приложения, когда настоящий синтаксический анализатор сидит в mainloop?

Ответ 1

С неблокирующими сокетами SSL_WANT_READ означает "дождитесь, когда сокет будет доступен для чтения, а затем снова вызовите эту функцию".; наоборот, SSL_WANT_WRITE означает "подождите, пока сокет будет доступен для записи, а затем снова вызовите эту функцию". Вы можете получить либо SSL_WANT_WRITE, либо SSL_WANT_READ от вызова SSL_read() или SSL_write().

Ответ 2

Вы прочитали документацию OpenSSL для ssl_read и ssl_get_error еще?

ssl_read:

Если базовый BIO блокируется, SSL_read() будет возвращаться только после операция чтения завершена или произошла ошибка, за исключением случаев, когда происходит пересмотр, в котором может произойти SSL_ERROR_WANT_READ. Такое поведение можно контролировать с помощью флаг SSL_MODE_AUTO_RETRY Вызов SSL_CTX_set_mode (3).

Если базовый BIO не блокируется, SSL_read() также вернется, когда базовый BIO не смог удовлетворить потребности SSL_read(), чтобы продолжить операция. В этом случае SSL_get_error (3) с возвращаемым значением SSL_read() даст SSL_ERROR_WANT_READ или SSL_ERROR_WANT_WRITE. Как и в любое время возможен пересмотр переговоров, призыв к SSL_read() также может вызвать запись операция! Затем вызывающий процесс должен повторить вызов после принятия соответствующие действия для удовлетворения потребности SSL_read(). Действие зависит от базового BIO. когда использование неблокирующего сокета, ничего, но select() может быть используется для проверки требуемого состояние.

ssl_get_error:

SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE

Операция не завершена; такая же функция ввода/вывода TLS/SSL должна быть снова называется позже. Если к тому времени базовый BIO имеет данные, доступные для чтение (если код результата SSL_ERROR_WANT_READ) или позволяет писать данных (SSL_ERROR_WANT_WRITE), затем некоторые Прогресс протокола TLS/SSL будет место, то есть, по меньшей мере, часть TLS/SSL запись будет считана или записана. Заметка что повтор может снова привести к SSL_ERROR_WANT_READ или SSL_ERROR_WANT_WRITE состояние. Нет фиксированного верхнего предела для количества итераций, которые могут необходимо, пока прогресс не станет видимый на уровне протокола приложения.

Для сокетов BIO (например, когда SSL_set_fd()), select() или poll() на базовая сокета может быть использована для поиска когда функция TLS/SSL I/O следует повторить.

Предостережение: любая функция ввода/вывода TLS/SSL может привести к SSL_ERROR_WANT_READ и SSL_ERROR_WANT_WRITE. В частности, SSL_read() или SSL_peek() могут записи данных и SSL_write() могут читать данные. Это в основном потому, что Связывание TLS/SSL может происходить на любом времени в течение протокола (по инициативе либо клиент, либо сервер); SSL_read(), SSL_peek() и SSL_write() будет обрабатывать любые ожидающие подтверждения.

OpenSSL реализуется как конечный автомат. SSL_ERROR_WANT_READ означает, что большее количество входящих данных и SSL_ERROR_WANT_WRITE означает, что требуется больше исходящих данных, чтобы добиться прогресса в соединении. Если вы получаете SSL_ERROR_WANT_WRITE в операции ssl_read(), вам необходимо отправить исходящие данные или, по крайней мере, дождаться, когда сокет станет доступен для записи. Если вы получаете SSL_ERROR_WANT_READ в операции ssl_write(), вам необходимо прочитать входящие данные.

Вам следует подписаться на рассылки OpenSSL. Этот вопрос задают много.

Ответ 3

SSL_WANT_READ означает, что механизм SSL не может в настоящее время шифроваться для вас, поскольку он ожидает дополнительных входных данных (либо как часть первоначального рукопожатия, либо как часть пересмотра), поэтому, как только ваше следующее чтение будет завершено, ve нажал данные, которые пришли через механизм SSL, вы можете повторить операцию записи.

Аналогично, SSL_WANT_WRITE означает, что движок SSL ожидает, что вы извлечете из него некоторые данные и отправите их равноправному узлу.

Я писал об использовании OpenSSL с неблокирующими и асинхронными сокетами еще в 2002 году для Windows Developer Journal (перепечатано здесь), и хотя эта статья якобы нацеленные на код Windows, принципы одинаковы для других платформ. В статье представлен код, который объединяет OpenSSL с асинхронными сокетами в Windows и который касается всей проблемы SSL_WANT_READ/SSL_WANT_WRITE.

По сути, когда вы получаете SSL_WANT_READ, вам нужно отправлять в очередь исходящие данные, пока вы не закончите чтение, и вы передали новые входящие данные в механизм SSL, как только это произошло, вы можете повторить отправку исходящих данных.