web-dev-qa-db-ja.com

非ブロッキングソケットでOpenSSL SSL_ERROR_WANT_READ / WANT_WRITEを処理する方法

OpenSSLライブラリでは、SSL_readを使用して基礎となるソケットから読み取り、SSL_writeを使用してソケットに書き込むことができます。これらの関数は、SSL接続の再ネゴシエーション時など、SSLプロトコルのニーズに応じて、SSL_ERROR_WANT_READまたはSSL_ERROR_WANT_WRITEで返される場合があります。

APIがこれらの結果で何をしたいのか本当に理解できません。

クライアント接続を受け入れるサーバーアプリをイメージングし、新しいsslセッションをセットアップし、基になるソケットを非ブロック化してから、ファイル記述子をselect/poll/epollループに追加します。

クライアントがデータを送信する場合、メインループはこれをssl_readにディスパッチします。 SSL_ERROR_WANT_READまたはSSL_ERROR_WANT_WRITEが返された場合、ここで何をする必要がありますか?次のメインループの反復が別のssl_readにつながる可能性があるため、WANT_READは簡単かもしれません。しかし、ssl_readがWANT_WRITEを返す場合、どのパラメーターを使用して呼び出す必要がありますか?そして、なぜライブラリは呼び出し自体を発行しないのですか?

サーバーがクライアントにデータを送信する場合は、ssl_writeを使用します。繰り返しますが、WANT_READまたはWANT_WRITEが返された場合はどうしますか?呼び出されたばかりのまったく同じ呼び出しを繰り返すことで、WANT_WRITEに応答できますか?そして、WANT_READが返された場合、メインループに戻り、select/poll/epollがこれを処理するようにしますか?しかし、最初に書かれるべきメッセージについてはどうですか?

または、失敗した書き込みの直後に読み取りを実行する必要がありますか?次に、実際のパーサーがメインループにあるときに、アプリケーションプロトコルからバイトを読み取り、アプリの周辺のどこかでそれを処理しなければならないのを防ぐにはどうすればよいでしょうか。

38
dantje

ノンブロッキングソケットの場合、_SSL_WANT_READ_は"ソケットが読み取り可能になるのを待ってから、この関数を再度呼び出します。";逆に、_SSL_WANT_WRITE_は"ソケットが書き込み可能になるのを待ってから、この関数を再度呼び出します。"を意味します。 _SSL_WANT_WRITE_または_SSL_WANT_READ_は、SSL_read()またはSSL_write()の両方の呼び出しから取得できます。

55
caf

ssl_read および ssl_get_error のOpenSSLドキュメントを読みましたか?

ssl_read:

基盤となるBIOがブロックしている場合、SSL_read()は、再ネゴシエーションが行われる場合を除いて、読み取り操作が完了するかエラーが発生した場合にのみ戻ります。この動作は、SSL_CTX_set_mode(3)呼び出しのSSL_MODE_AUTO_RETRYフラグで制御できます。

基礎となるBIOが非ブロッキングの場合、基礎となるBIOが操作を続行するためのSSL_read()のニーズを満たせなかった場合にも、SSL_read()は戻ります。この場合、SSL_read()の戻り値を指定してSSL_get_error(3)を呼び出すと、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 I/O関数を後で再度呼び出す必要があります。それまでに、基礎となる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 I/O関数は、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メーリングリスト を購読する必要があります。この質問はたくさん聞かれます。

16
Remy Lebeau

SSL_WANT_READは、(最初​​のハンドシェイクの一部として、または再ネゴシエーションの一部として)追加の入力データを待機しているため、SSLエンジンが現在暗号化できないことを意味します。したがって、次の読み取りが完了し、 SSLエンジンを介して到着したデータは、書き込み操作を再試行できます。

同様に、SSL_WANT_WRITEは、SSLエンジンがデータを抽出してピアに送信するのを待っていることを意味します。

私は、2002年にWindows Developer Journalで非ブロッキングおよび非同期ソケットでOpenSSLを使用することについて書き直しました(転載 here )。この記事はWindowsコードを目的としているが、他のプラットフォームでも原則は同じです。この記事には、OpenSSLをWindowsの非同期ソケットと統合し、SSL_WANT_READ/SSL_WANT_WRITEの問題全体を処理するコードが付属しています。

基本的に、SSL_WANT_READを取得したら、読み取りが完了し、新しい受信データをSSLエンジンに渡すまで送信データをキューに入れる必要があります。これが発生したら、送信データの送信を再試行できます。

7
Len Holgate