前回までのあらすじ。
- `nodertc/nodertc`を読んでた
- クライアントとSessionを確立する際に、内部的にいくつかサーバーを立ててた
- STUN: 前回読んだ
- DTLS: 今回はコレ
- SCTP
- こいつらの詳細を読み進めているところ
というわけで、今回はDTLSの部分。
書いてみたら長くなったので、前後編になってます。
使われ方
// STUNの最初の検証が済んでから動く // this.stun.once(STUN_EVENT_BINDING_RESPONSE, () => this.startDTLS()); startDTLS() { console.log('[nodertc][dtls] start'); const options = { socket: this[_usocket], certificate: this[_certificate], certificatePrivateKey: this[_privateKey], checkServerIdentity: certificate => fingerprint(certificate.raw, 'sha256') === this[_peerFingerprint], }; this[_dtls] = dtls.connect(options); this.dtls.once('connect', () => { console.log('[nodertc][dtls] successful connected!'); }); this.dtls.on('error', err => { console.error('[nodertc][dtls]', err); }); this.startSCTP(); }
というわけで、特別なにかしてるわけではない。土管。
nodertc/dtls
読んだバージョンは`0.5.0`です。
GitHub - nodertc/dtls: Secure UDP communications using DTLS.
`src/index.js`にはじまり、まあ本体はSTUNと同じで`node_modules`というディレクトリの中にある・・。
node_modules ├── cipher │ ├── abstract.js │ ├── aead.js │ ├── defaults.js │ ├── key-exchange.js │ ├── null.js │ └── utils.js ├── filter │ ├── decoder.js │ ├── defragmentation.js │ └── reordering.js ├── fsm │ ├── protocol.js │ ├── retransmitter.js │ └── states.js ├── lib │ ├── constants.js │ ├── protocol.js │ ├── sender.js │ ├── sliding-window.js │ └── socket.js ├── session │ ├── abstract.js │ ├── client.js │ └── utils.js └── utils └── debug.js
さすがTLS関連の実装だけあってファイルが多い!
取っ掛かりはモジュールとして、`lib/socket.js`からエクスポートしてる`connect()`関数。
ただ`connect()`では`new Socket(options).connect()`しかしてなかったのでそっちがメイン。
上のレイヤーから渡されてるのは、`unicast`のソケットなので、それも忘れずに追う。
class: Socket
`constructor()`でやってること。
- extends `Duplex`
- つまり extends `EventEmitter`でもある
- `ClientSession`を`session`として初期化
- `session.on('data', packet => this.push(packet)` = 自身の`Duplex.push()`
- 'session.on('handshake:finish', () => this.emit('connect'))`
- 各種`Stream`を`pipeline()`でつないでる
- `pipeline(writer, socket, onerror);`
- `pipeline(socket, isdtls, decoder, reorder, defrag, protocol, onerror);`
というわけでこのクラスでは、
- `ClientSession`がやってること
- `Stream`の`pipeline()`群
- `connect()`の中身
この3つを追えばよさそう。
class: ClientSession
その名の通り、DTLSのセッションの本体であり一番実装が重そうなところ。
`session/client.js`が実態、`session/abstract.js`を継承してて、`session/utils.js`が関数群。
`AbstractSession`と`ClientSession`からわかるのは、
- DTLSのバージョンは`1.2`
- DTLSなので暗号化まわりのコード
- `NullCipher`
- `retransmitter`は再送制御
- `SlidingWindow`とかも自前実装で持ってる
- etc...
このあたりはざっくりでもDTLSの実装フローを知ってないと読み解けなさそう。
今のところ、副作用のあるコードにはたどりつけてないので、`session`を引数にもらってる登場人物各種がどこかできっかけを作るはず。
`socket.connect()`
- `constructor()`で初期化した`ProtocolReader(session)`の`start()`を呼んでる
- `fsm/protocol.js`
- この`start()`で、`session.startHandshake()`を呼んでた
`ProtocolReader`をみていく。
class: ProtocolReader
続 ClientSession
`Socket`で初期化された`session`は、登場人物各種にも引数として渡されてる。
- Socket - ClientSession - ProtocolReader - Sender - Decoder - Reordering - Defragmentation
`Socket`の`constructor()`で渡される依存関係はこんな感じ。
`Defragmentation`だけ`session`を受け取らない。
`ClientSession`内の目ぼしいクラスは以下。
- `RetransmitMachine`
- `fsm/retransmitter.js`
- `SlidingWindow`
- `lib/sliding-window.js`
class: RetransmitMachine
- これも内部的にStateとQueueを持って再送を制御してる
- 基本的にRFC参照って書いてある
class: SlidingWindow
- Anti-Replayの仕組み
- 逐次シーケンスNoを入れてあって、なんかのタイミングでチェックしてる
- 並び替えとか再送とか
後編へつづく
長くなってきたのでこのへんで。
今回はDTLSソケットがつながる過程で用意される`ClientSession`でやってることの途中まで読んだ。
長くなりそうな`pipeline()`をつなげてるところは後編で。