Hatena::ブログ(Diary)

◆F99a.q8oVEの日記 Twitter

2011-07-10

SSL_read, SSL_write での SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE と、SSL_pending の話

TLS/SSL による通信をやりたい場合、OpenSSL を使えば簡単に実装することができる。

具体的には、recv(2), send(2) を直に発行するノリで、SSL_read(3), SSL_write(3) を使えばいい……と思っていたが、そうではないらしい。

ということで、調べたことをつらつら書いてみる。なお、以下は socket が non-blocking であることを想定して書いている。

それから、OpenSSL をそこまで読み込んだわけではないので、間違っているかもしれない。ツッコミ大歓迎!


SSL_read での SSL_ERROR_WANT_READ. SSL_write での SSL_ERROR_WANT_WRITE

データの送信を行おうとしたときに TCP/IPバッファがいっぱいで積めない場合、send は EAGAIN でエラーする。

同様に SSL_write は SSL_ERROR_WANT_WRITE を返す。この時、socket が writable になったら、全く同じ引数で SSL_write を呼ばなければならない。

WARNING

When an SSL_write() operation has to be repeated because of

SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated with

the same arguments.

When calling SSL_write() with num=0 bytes to be sent the behaviour is

undefined.


recv の場合は、TCP/IP のバッファが空であった場合に EAGAIN でエラーする。

同様に SSL_read は SSL_ERROR_WANT_READ でエラーする。この時 SSL_write と同じで、socket が readable になったら、全く同じ引数で SSL_read を呼ばなければならない。

ここで注意しなければならないのが、select(2) で readable だったとしても、SSL_read が SSL_ERROR_WANT_READ でエラーすることがあるという点。

TLS/SSL では、データはある程度ブロックに分けられた上で、暗号化されて送受信されているため、1ブロック分全部そろっていないとデコードできない。よって、中途半端にデータを受信した場合にも SSL_ERROR_WANT_READ が返るということになる。


SSL_read での SSL_ERROR_WANT_WRITE. SSL_write での SSL_ERROR_WANT_READ

ややこしい事に、先ほどとは逆の理由でエラーすることがある。

OpenSSL では、SSL_read, SSL_write の延長上でネゴシエーション処理が実行されることがあるために、read しようとしたのに WANT_WRITE, write しようとしたのに WANT_READ が返ってきてしまう。

よって、先程の仕様とあわせると、

  • SSL_ERROR_WANT_READ: readable になってから関数を再実行
  • SSL_ERROR_WANT_WRITE: writable になってから関数を再実行

しないといけない。

すなわち……

SSL_read SSL_write
SSL_ERROR_WANT_READ でエラー select(readable), SSL_readselect(readable), SSL_write
SSL_ERROR_WANT_WRITE でエラーselect(writable), SSL_read select(writable), SSL_write

これはメンドクサイですね…。


SSL_pending

更に、SSL_pending という物がある。

この関数は、ssl オブジェクトのバッファから block せずに読み取り可能な byte 数を返すものである。

ここで注意しなければならないのが、socket が readable ではない場合にも ssl オブジェクトのバッファにデータが乗っていることがあるということである。*1

これを考慮すると、select で readable か判定する前に SSL_pending で SSL_read *2可能かどうかチェックする必要性が見えてくる。

ただし、次の記載が man に有って、少々気にはなる。

BUGS

(snip)

Up to OpenSSL 0.9.6, SSL_pending() does not check if the record type of

pending data is application data.

*1:ネゴシエーション処理などの関係で、らしい。

*2:SSL_pending は受信バッファなので、SSL_write は無関係