要約

  • RFC 2543のbranchは主にforkした複製を区別する印だった。RFC 3261はそれを必須とし、原則として時空間で一意なtransaction識別子へ変えた。
  • z9hG4bK は認証情報ではなく互換性マーカーである。存在すれば新しい照合を、なければ複数fieldを使う旧手順を選べる。
  • 同じ試行の再送はbranchを保ち、新しい宛先への試行は別の値を持つ。CANCELと非2xx ACKだけは相関のため意図的に再利用する。

かつてbranchが区別したのはforkの内側だけだった

SIP requestは複数のproxyを通る。各hopは先頭にViaを積み、responseはその層を逆順にたどる。stateful proxyが一つのINVITEを複数の宛先へ分ければ、返ってきた結果を外向きの試行ごとに整理しなければならない。

1999年の RFC 2543 は、この用途に branch を置いた。forkするproxyには必須だったが、一方向だけへ送るproxyでは任意だった。一意性も、同じforkで作られたisomorphic requestの集合内で足りた。

responseの照合にはTo、From、Call-ID、CSeqと最初のVia branchを組み合わせた。つまりbranchは便利な枝札ではあっても、受信した任意の値がtransaction全体で一意だと宣言する証拠ではなかった。

同じfield名のまま意味だけを強くすると、旧実装と新実装が交わる場所で推測が必要になる。送信側は自分が守った規則を知るが、推測を誤ってstateを変更するのは受信側である。

7文字が適用すべき規則を示した

RFC 3261 は2002年、UACが作るrequestの先頭Viaにbranchを必須とした。CANCELと非2xx responseへのACKという限定例外を除き、その値はUAが送るrequestの間で時空間にわたり一意でなければならない。

そのbranchは z9hG4bK で始まる。文字列は時刻、vendor、user、経路を符号化しない。RFC 2543実装が偶然選ぶには十分に不自然な7文字として、新しい生成規則を示す。

magic cookieという呼び名に反して、秘密でもchallengeでもない。解釈が変わったfieldの中に、互換集合を明記したのである。receiverは相手の所属や製品名を信じず、messageを見てlocalに規則を選べる。

新方式でも一つのtokenだけを信じない

cookie付きrequestをserver transactionへ照合する条件は、先頭Viaのbranch、その sent-by、methodである。ACKには定義済みの例外がある。別clientが誤って、あるいは故意に同じbranchを使う可能性があるため、sent-byも残る。

cookieがなければ、Request-URI、To/From tag、Call-ID、CSeq、Viaを使うRFC 2543互換手順へ戻る。旧branchに後から強い約束を付け足さず、新branchを古い曖昧さへ引き戻さない設計である。

response側はbranchとCSeq methodを使う。CANCELは対象requestとbranchを共有しながら別transactionなので、methodが両者を分ける。RFC 3665 のcall flowでは、各proxyがViaとbranchを追加し、responseが戻る際に自分の層を外す。branchはcall全体ではなく、隣接するclientとserverのtransactionを指す。

再送は同じ値、別の試行は別の値

stateful proxyが別のtargetを試すたび、新しいclient transactionとbranchが生まれる。一方、同じ試行の再送は値を変えてはならない。既存stateに着地してresponseを再送させるためであり、新しい処理を起動するためではない。

stateless proxyには記憶がない。受信のたび乱数を振れば、同じrequestの再送を別transactionへ割ってしまう。そのためRFC 3261は、再送で変わらないmessage fieldとtime-invariantな設定から安定部分を導くよう求める。一意性だけでなく再現性も必要である。

loop検出を行うproxyは、処理判断に影響する入力をbranchの別部分へ反映できる。同じ条件で戻るrequestはloopかもしれないが、Request-URIなどが変わった再訪は正当なspiralになり得る。hashは判断材料を圧縮するだけで、入れなかった事実まで証明しない。

CANCELは対象を共有しても完了を共有しない

CANCELは対象requestのRequest-URI、Call-ID、tag、CSeq番号、Route、先頭Viaとbranchを写し、CSeq methodだけをCANCELにする。これでstateless proxyも元requestと同じ経路判断を再現できる。

しかしCANCEL自身は独立transactionである。CANCELへの200は取消requestを処理したことを示すが、元のINVITEには487、既に決まった別のfinal response、または固有のtimeoutが残る。RFC 3261はRFC 2543で混在していた二つの終了を分離した。同じ参照先は同じ結果を意味しない。

ACKの所属は2xxを境に変わる

非2xx final responseへのACKはINVITE transactionに含まれる。INVITEのViaとbranchを再利用し、methodをACKへ変え、失敗を扱ったtransaction state machineが吸収する。

2xxではfork先が複数受諾する場合がある。すべての成功をUACへ届けるため、ACKはproxyごとのINVITE transaction外でUA coreがend-to-endに扱い、新しいVia branchを構成する。成功と失敗の非対称性は、信頼性の責任主体が違うために生じた。

後の修正は保持時間を変え、識別規則を残した

RFC 4320 はnon-INVITE transactionのresponseとtimeoutを修正した。RFC 6026 はAccepted stateとTimer Lを加え、2xx送信後のINVITE再送を既存stateで吸収できるようにした。

RFC 6026はcookieを持たない旧ACKにも特別手順を残す。stateをいつ捨てるかと、messageを何に照合するかは別問題だからである。RFC 5359 のservice exampleは後代の記述例であって、世界の実装率を測ったものではない。

markerが証明しなかったこと

z9hG4bK は誰でも書ける。senderを認証せず、callを許可せず、完全性を守らず、後続部分の真の一意性も監査しない。transaction matchが正しくても、dialog、user identity、route全体、media negotiation、通話品質は別の証拠を要する。

歴史的な強さは、この狭さにある。senderは従った規則を可視化し、receiverは自分がstateを動かす境界で検証する。markerは解釈を選ぶが、callに対する権力を得ない。

出典と証拠の限界

閉じた出典集合は RFC 2543、RFC 3261、RFC 3665、RFC 4320、RFC 5359、RFC 6026 である。規則、例、変更履歴は示すが、現在の製品準拠率、deployment share、通話品質、collision頻度は示さない。