はじめに
「ssl_get_errorは何を確認するための関数なの?」
「SSL_ERROR_WANT_READやSSL_ERROR_SSLが返ってきたけれど、どのように対処すればいい?」と迷っていませんか。
OpenSSLを使ったプログラムでSSL_connectやSSL_read、SSL_writeが失敗し、戻り値だけでは通信を再試行するべきか、接続を終了するべきか判断できず迷うことがありますよね。
この記事では、ssl_get_errorの基本的な使い方から、主なエラーコードの意味、コードごとに確認したい対処方法まで紹介します。
SSL_get_errorとは?

SSL_get_errorは、SSL_readやSSL_writeなどを実行したあと、その戻り値からSSL/TLS処理がどの状態になったのかを判定するための関数です。
そのため、まずSSL_get_errorの関数シグネチャと役割を確認し、続いてSSL_read・SSL_writeなどの戻り値をどのように渡して判定するのかを見ていきます。
SSL_get_errorの関数シグネチャと基本的な役割
SSL_get_errorの関数シグネチャは「int SSL_get_error(const SSL *ssl, int ret);」です。
第1引数には対象のSSLオブジェクト、第2引数には直前に実行したSSL_readやSSL_writeなどの戻り値を指定します。
この2つを基に、処理の成功や再試行の必要性、接続終了、エラーの状態などを確認できます。
SSL_read・SSL_writeなどの戻り値を渡して判定する
SSL_readやSSL_writeを実行したあとは、その戻り値を変更せずにSSL_get_errorへ渡して状態を確認します。
たとえば「int ret = SSL_read(ssl, buf, sizeof(buf));」のあとに「int err = SSL_get_error(ssl, ret);」とすれば、処理結果を適切に判定できます。
戻り値だけで判断せず、同じSSLオブジェクトと組み合わせて確認することが大切です。
SSL_get_errorの基本的な使い方
SSL_get_errorを使うときは、SSL_readやSSL_writeを実行して戻り値を取得し、その値をSSL_get_errorへ渡して処理結果を判定します。
ここでは、SSL_get_errorを呼び出す基本手順を確認したうえで、SSL_readとSSL_writeそれぞれのエラー判定をコード例に沿って説明します。
SSL_get_errorを呼び出す基本手順
SSL_readやSSL_writeを実行したら、まず戻り値を変数に保存します。
戻り値が0以下の場合は、同じSSLオブジェクトと戻り値をSSL_get_error(ssl, ret)へ渡して状態を確認しましょう。
返された値に応じて、再試行するのか、接続終了として扱うのか、エラー処理へ進むのかを判断します。
SSL_readでエラーを判定するコード例
SSL_readでは、読み取り結果をretへ保存し、0以下だった場合にSSL_get_errorへ渡します。
SSL_ERROR_WANT_READまたはSSL_ERROR_WANT_WRITEなら、その状態に応じて読み書きが可能になるまで待ってから再試行します。
コードでは次のように判定できます。
int ret = SSL_read(ssl, buf, sizeof(buf));
if (ret <= 0) {
int err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_READ ||
err == SSL_ERROR_WANT_WRITE) {
/* 読み書き可能になるまで待って再試行 */
} else {
/* 接続終了またはエラーとして処理 */
}
}
SSL_writeでエラーを判定するコード例
SSL_writeでも、書き込み結果をretへ保存し、0以下だった場合にSSL_get_errorへ渡します。
SSL_ERROR_WANT_WRITEまたはSSL_ERROR_WANT_READなら、その状態に応じて読み書きが可能になるまで待ってから再試行します。
コードでは次のように判定できます。
int ret = SSL_write(ssl, buf, len);
if (ret <= 0) {
int err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_WRITE ||
err == SSL_ERROR_WANT_READ) {
/* 読み書き可能になるまで待って再試行 */
} else {
/* 接続終了またはエラーとして処理 */
}
}
SSL_get_errorで返される主なエラーコードと意味
SSL_get_errorの戻り値は、SSL/TLS処理が正常に完了したのか、接続が終了したのか、再試行やエラー確認が必要なのかを判定するために使います。
接続処理中にはSSL_ERROR_WANT_CONNECTやSSL_ERROR_WANT_ACCEPTが返される場合もあるため、それぞれの意味と返されたあとの対応を確認していきます。
SSL_ERROR_NONEはエラーが発生していない状態
SSL_ERROR_NONEは、SSL/TLS処理が正常に完了し、エラーが発生していない状態を示します。
SSL_readならデータの読み取り、SSL_writeなら書き込みが正常に行われたと判断できます。
この場合はエラー処理や再試行を行わず、戻り値に従って次の処理へ進みます。
SSL_ERROR_ZERO_RETURNはTLS接続が正常終了した状態
SSL_ERROR_ZERO_RETURNは、相手からTLSのclose_notifyを受信し、接続が正常に終了した状態を示します。
通信障害による突然の切断ではなく、TLSの手順に沿って終了した場合に返されます。
この値を確認したら、データの読み書きを続けず接続終了として処理します。
SSL_ERROR_WANT_READ・SSL_ERROR_WANT_WRITEは処理を再試行する
SSL_ERROR_WANT_READは読み取り、SSL_ERROR_WANT_WRITEは書き込みが可能になるまで待つ必要がある状態です。
すぐに通信失敗とは判断せず、必要なI/Oが可能になってから処理を再試行します。
ノンブロッキングI/Oでは、selectやpollなどでソケットの状態を待ってから処理を続けます。
SSL_ERROR_SYSCALLはOSやI/Oレベルのエラーを確認する
SSL_ERROR_SYSCALLは、SSL/TLS処理中にOSやI/Oに関係するエラーが発生した可能性を示します。
この場合はOpenSSLのエラーキューやerrnoなどを確認し、原因を調べます。
SSL_ERROR_SYSCALLだけで判断せず、戻り値やOS側のエラー情報もあわせて確認しましょう。
SSL_ERROR_SSLはOpenSSLのエラー情報を確認する
SSL_ERROR_SSLは、SSL/TLSプロトコルやOpenSSL内部の処理でエラーが発生した状態を示します。
この値が返された場合は、ERR_get_errorなどを使ってエラーキューの情報を取得します。
記録されている内容を確認しながら、SSL/TLS処理が失敗した原因を調べましょう。
SSL_ERROR_WANT_CONNECT・SSL_ERROR_WANT_ACCEPTは接続処理を継続する
SSL_ERROR_WANT_CONNECTやSSL_ERROR_WANT_ACCEPTは、接続や接続受け入れの処理がまだ完了していない状態を示します。
エラーとして終了するのではなく、必要なI/Oが可能になるまで待って処理を再試行します。
接続が完了するまで同じ処理を続け、その都度戻り値を確認しましょう。
SSL_get_errorを使うときの注意点
SSL_get_errorで正しく状態を判定するには、対象となるSSL/TLS処理の前後でOpenSSLのエラー情報を適切に扱う必要があります。
処理前のエラーキューの状態やOpenSSLのバージョンによるunexpected EOFの扱いも確認し、判定結果を誤らないための注意点を順番に見ていきます。
対象処理とSSL_get_errorの間で別のOpenSSL関数を呼ばない
SSL_readやSSL_writeを実行したあとは、別のOpenSSL関数を挟まずにSSL_get_errorを呼び出します。
途中で別の関数を実行するとエラーキューの内容が変わり、正しく判定できない可能性があります。
対象処理の戻り値を保存したら、そのままSSL_get_errorへ渡しましょう。
SSL_get_errorは対象処理と同じスレッドで呼び出す
SSL_get_errorは、SSL_readやSSL_writeを実行したスレッドと同じスレッドで呼び出します。
OpenSSLのエラーキューはスレッドごとに管理されているため、別のスレッドでは対象処理の状態を正しく判定できない可能性があります。
対象処理からエラー判定までを同じスレッド内で行いましょう。
処理前にOpenSSLのエラーキューを空にしておく
SSL_get_errorで正しく状態を判定するには、SSL_readやSSL_writeなどを実行する前にエラーキューを空にしておくことが大切です。
以前のエラーが残っていると、その情報が今回の判定に影響する可能性があります。
対象処理を始める前にエラーキューが空の状態になっていることを確認しましょう。
OpenSSLのバージョンによってunexpected EOFの扱いが異なる
close_notifyを受信せずに接続が切れたunexpected EOFは、OpenSSLのバージョンによって扱いが異なります。
OpenSSL 1.1.1ではSSL_ERROR_SYSCALLとなる場合がありますが、3.0以降ではSSL_ERROR_SSLとなり、エラーキューにSSL_R_UNEXPECTED_EOF_WHILE_READINGが記録されます。
判定するときは、使用しているOpenSSLのバージョンもあわせて確認しましょう。
SSL_get_errorでエラー原因を切り分ける方法
SSL_get_errorの戻り値を確認したあとは、エラーコードに応じて次に調べる箇所を切り替えます。
ここでは、それぞれのエラーコードに応じた確認方法と、コードだけでは原因を特定できない場合の切り分け手順を順番に説明します。
WANT_READ・WANT_WRITEなら読み書き可能になるまで待つ
SSL_ERROR_WANT_READが返された場合は読み取り可能になるまで、SSL_ERROR_WANT_WRITEなら書き込み可能になるまで待ってから処理を再試行します。
ノンブロッキングI/Oでは、selectやpollなどでソケットの状態を確認するとよいでしょう。
準備が整ったら、同じSSL/TLS処理をもう一度実行します。
SSL_ERROR_SYSCALLならerrnoなどを確認する
SSL_ERROR_SYSCALLが返された場合は、errnoなどのOS側のエラー情報を確認します。
ソケットの読み書きでシステムコールが失敗していれば、errnoから接続切断やI/Oエラーの原因を調べられます。
SSL_ERROR_SYSCALLだけで判断せず、対象処理の戻り値とあわせて確認しましょう。
SSL_ERROR_SSLならエラーキューから詳細を確認する
SSL_ERROR_SSLが返された場合は、OpenSSLのエラーキューを確認します。
ERR_get_errorでエラーコードを取得し、ERR_error_stringなどを使うと内容を確認できます。
複数のエラーが記録されている場合は、キューが空になるまで確認して原因を絞り込みましょう。
エラーキューを確認しても内容の意味が分かりにくい場合は、OpenSSLで発生したエラーの読み方も確認しておくと原因を追いやすくなります。
▶OpenSSLのエラーコードとは?確認方法と原因の調べ方
エラーコードだけで原因を判断できない場合の確認手順
SSL_get_errorだけで原因が分からない場合は、まずSSL_readやSSL_writeなどの戻り値を確認します。
続いてSSL_ERROR_SYSCALLならerrno、SSL_ERROR_SSLならエラーキューを確認すると、原因を絞り込みやすくなります。
それでも分からない場合は、戻り値やエラー情報をログに残し、どの処理で問題が起きているのか確認しましょう。
SSL/TLS通信そのものがどの段階で失敗しているのか分からない場合は、ハンドシェイクの流れから確認すると切り分けやすくなります。
▶SSL/TLSハンドシェイクとは?通信の流れとエラー原因をわかりやすく解説
まとめ
SSL_get_errorを使うと、SSL_readやSSL_writeの戻り値だけでは分かりにくいSSL/TLS処理の状態を確認できます。
エラーが出たときはすぐに通信失敗と判断せず、返された値に合わせて再試行したり、errnoやOpenSSLのエラー情報を確認したりすることが大切です。
原因が分からない場合も、戻り値から順番に確認していけば問題を切り分けやすくなるため、基本的な使い方と注意点を押さえて活用していきましょう。











