アプリケーションプロトコル設計のベストプラクティス (over TCP)

みたいなものを誰かまとめてないのかな、と思ってる。ないのかな。とりあえず自分用メモ。

概要

  • 接続後最初の電文は client => server であるべき
    • 理由: syn_cookies, dataready, パケット数削減 (w. SYN ACK)
  • 最初の電文は、プロトコルの magic で始めるべき
    • 理由: gopher://host:25/ attack
    • バージョン番号も含めるべき
      • 何がコンパチで何が非互換か、最初からバージョン番号の解釈を明確にしておくこと
      • 例: HTTP
  • extensibleな構造化の手法は3種類
    • デリミタ方式
    • TLV
    • ASN.1 PER

デリミタ方式

テキストプロトコル系で一般的。ペイロード内にデリミタが出現する場合はエスケープ。

  • 問題は遅いこと
    • メタ情報のみデリミタでコンテンツは length というモデルも (HTTP)
  • デリミタの定義をはっきりさせること

TLV

type,length,valueのペアを連ねて表現。バイナリプロトコルで一般的。

  • 解析が面倒
    • 内側のlengthの合計は外側のそれと等しいか、等

その他バイナリ系

Type 等のコンテクストによって Value のパーサが決まるので length が不要だったりして (あまり) 冗長じゃないプロトコル。ASN.1 PER 系や DNS 等。

  • いろいろ支援機構がないと辛い
    • 解析が、とても面倒
      • 全体を理解するパーサを作らないと、部分的な解析すらできない
  • UDP 1パケットに収めたいといった場合に便利

構造化の留意点

  • レイヤをきちんと分けること
    • 上位レイヤ (例: コマンド名) によって、下位レイヤの処理を変えるのはダメ
      • 反例: STOMP の ERROR メッセージ
  • required と optional をはっきりさせること
    • 電文内のフラグで表現可能にする場合は、「未知のフィールドがrequiredだったらfatal」に限定すべき
      • otherwise security problems arise
  • PER のような方式でない限り、値が複数回指定できる構造化方式にならざるを得ない点に留意
    • 1回しか指定されるべきでない値が複数回指定された場合、最初のものを優先するのか最後のものを使用するのか明確にすべき
  • 複数の構造化方法の混在は危険
    • 反例: HTTP splitting attack using content-length and content-encoding

最適化

  • TLVにせよデリミタにせよ、バッファリングが必要
    • パイプライン化を仕様で禁止するなら、なくてもなんとかなるけどさ
  • バイナリプロトコルなら、1バイトの電文を駆使するという技も
    • ACK,NCK 等で有効

テキストプロトコル文字コード

パイプライン処理

  • パイプライン処理を認める場合、close(2) は禁止
    • タイミングによって RST が発生し、その結果、電文が欠落するため
    • 参考: Connections in FIN_WAIT_2 and Apache
    • パイプラインをやるなら、必ず shutdown(WR) して相手が close するのを待つこと
  • latency がそれほど問題にならないイントラネットプロトコルでは、パイプラインを禁止するのも手

以下続く、かもしれない。