SIPレジストを使って、Twilioで固定電話をつかってみる

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# SIPレジスト

<font color='red'>2020/12/30 機能が拡充されたため更新</font>

## 概要

SIPレジストとは、Twilioに対してSIP端末（SIP電話機、SIPアプリケーション）をレジストレーションすることができる機能です。 これにより、外部にSIPサーバーがなくても、Twilioを使ってSIP端末を利用した発着信が可能となります。 特に、市場に多く存在するSIP電話機（固定電話）を直接Twilioに収容することができ、従来のブラウザやスマートフォンのVoice Clientに加えて、固定電話をTwilioのVoice端末として利用できます。 ~~現在は、US1（バージニア）リージョンのみ利用が可能ですが、~~日本の050番号をアサインしての受発信も可能です。

<font color='red'>2020/12/30 追記</font>
従来、SIPレジストはUS1のみ対応でしたが、現在は [Edge Locations](https://www.twilio.com/docs/global-infrastructure/edge-locations) に対応しています。そのため、日本で利用するためには、リージョンに`tokyo`を指定することで、番号が非通知にならず、遅延も少なく通話が可能になります。

## 用語解説

### SIPドメイン

Twilio上で一意になるようなSIPグループの識別子 
SIPドメインは以下のように決定される 
```[SIPドメイン].sip.twilio.com``` 
[SIPドメイン]部分は、ユーザが独自に決めることができるが、Twilio全体で一意になる必要性がある 
なお、SIPレジストは、SIPドメインに付随するオプション機能として提供されている

### IPアクセスコントロールリスト

Twilioに対してSIP接続をする端末のグローバルアドレスのホワイトリスト 
SIPレジストを使ってTwilioにレジストレーションする際は、ここで設定したIPアドレスからアクセスする必要がある

### クレデンシャルリスト

SIPで利用されるクレデンシャル（ユーザ名とパスワードの組み合わせ）のリスト 
パスワードは、大文字、小文字、数字を最低1文字以上ずつ利用した12文字以上で設定する必要がある

## SIPレジストを使った場合の発着信フロー

### レジストレーション

レジストレーションとは、SIP電話機をTwilioのSIPドメインに登録する作業です。 レジストレーション作業は通常、SIP電話機の電源を入れたときなどに、SIP電話機側から行われます。 レジストレーションに必要な情報は、以下のとおりです。

- ユーザ名(クレデンシャルリストで登録）
- パスワード（クレデンシャルリストで登録）
- サーバーホスト（[SIPドメイン].sip.[EdgeLocation].twilio.com）
- サーバーポート（5060を指定）

### SIPレジストを使った発信フロー

レジストが完了したSIP電話機から発信をすると、SIPドメインの設定内にある音声通話設定の[REQUEST URL]に対して、TwilioがWebHookを発行します。 
その際に、Twilioからユーザ側のサーバーに対して、以下のような形式で発信先情報が通知されます。 
```sip:[発信先電話番号]@[sipドメイン].sip.[EdgeLocation].twilio.com```
 WebHookを受け付けたユーザ側のサーバーは、発信先電話番号を判定して、通常の通話と同じように、Dial動詞などがかかれたTwiMLを返却します。 たとえば、発信先の電話番号がPSTNの番号であれば、Numberを使って発信し、発信先の電話番号が内線の番号（桁数や先頭の数字などで判定するのが一般的です）であれば、Sipを使って発信します。 
発信の際の発信者番号も、PSTN宛であれば050番号に、内線宛であれば、内線番号を使ったSIPアドレスにするなど、相手先に応じて変更してあげると掛け直しができるので親切です。

### SIPレジストを使った着信フロー

SIP電話機に対する着信には、PSTN経由の着信と、SIP電話機からの着信の2パターンが考えられます。 
PSTN経由での着信のためには、通常の着信と同じように、予め050番号を用意しておく必要があります。
具体的には、050番号の設定にある音声通話の[A CALL COMES IN]の設定にWebHookのURLを指定します。 TwilioからのWebHookを受けたユーザー側のサーバーでは、例えば以下のようなDial動詞とSipを組み合わせたTwiMLを返します。この際、callerIdを指定しないでおくと、発信者の番号が通知されます。

<font color='red'>2020/12/30 追記</font>
従来、Dial動詞の内側には、Sipを一つしか記載できませんでしたが、現在は最大10個までSipを記載することができます。このようにすると、同時に最大10台のSIP端末を呼び出すことができます。複数台の端末を呼び出したときは、最初に取った端末だけが通話が可能となります。 
なお、SIPを列挙する場合は、[Voiceの設定画面](https://jp.twilio.com/console/voice/settings)で `Enhanced Programmable SIP Features` を有効にする必要があります。
![スクリーンショット 2020-12-30 6.34.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/f12963ed-bba2-3efd-9551-e60c91800537.png)


```
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial>
    <Sip>sip:[ユーザA]@[sipドメイン].sip.twilio.com</Sip>
    <Sip>sip:[ユーザB]@[sipドメイン].sip.twilio.com</Sip>
    // 最大10台まで記載が可能
</Dial>
</Response>
```

## ハンズオン

今回は、Yearlinkという会社の固定電話（Yealink T21P E2）を使って、固定電話を使った電話の受発信をやりたいと思います。
ハンズオンに先立ち、予め以下の内容を決めておいてください。

- 050番号：外線に発信する際の発信者番号、着信するための番号として、050番号を一つ購入しておきます。
- SIPドメイン：全世界でユニークなるような文字列を考えておきます。
- IPアドレス:電話機を設置するネットワークのグローバルアドレスです。[ここ](https://www.cman.jp/network/support/go_access.cgi)にアクセスすると、現在のグローバルアドレスがわかります。
- SIPユーザ名:今回は9から始まる４桁の内線番号をユーザ名にします。
- SIPパスワード:パスワードは12文字以上で、すくなくとも１つ以上の大文字、小文字英字、数字を含む必要があります。

### Twilio側の設定

#### SIPドメインの作成

1. 管理コンソールにログインし、プログラマブルVoiceを選択する。
2. SIPドメインを選択し、IPアクセスコントロールリストを選択する。
3. +アイコンを押下し、新しいリストを作成する。
4. フレンドリーネームにわかりやすい名前をつけて、＋アイコンを押下し、新しくIPアドレスを作成する。
5. IPアドレス欄に、IP電話機を設置するネットワークのグローバルアドレスを入力し、フレンドリーネームに何かわかりやすい名前をつける。
6. 保存ボタンを押して、IPアクセスコントロールリストを保存する。
7. SIPドメインの中のクレデンシャルリストを選択する。
8. +アイコンを押下し、新しいクレデンシャルリストを作成する。
9. フレンドリーネームにわかりやすい名前をつけて、ユーザ名とパスワードを設定する。

### IP電話機（Yealink T21P E2）のセットアップ

#### 接続

1. IP電話機のINTERNETポートをVoIPネットワークに接続する。
2. PoEハブがない場合は、ACアダプタを接続する（電源が入る）。
3. 設定用PCも同様に、VoIPネットワークに接続する。
4. IP電話機が起動したら、設定→1.状態（次へ）と押下し、IPアドレスを調べる。
5. 設定用PCのブラウザで、IP電話機のアドレスを開く。
6. ユーザ名、パスワードを入力してログインする（初期設定は、ID:admin、PASS:admin）。

#### アカウント設定

1. [アカウント]タブを開き、アカウント１を選択する（T21P E2は最大2つまでアカウント登録が可能）。
2. 以下の項目を設定する。

- ライン有効：有効
- ラベル：内線番号
- 表示名：内線番号
- 登録名：内線番号
- ユーザ名：SIPユーザ名（今回は内線番号）
- パスワード：SIPパスワード
- SIPサーバ1：[SIPドメイン名].sip.~~us1~~tokyo.twilio.com
- ポート：5060
- あとはデフォルト値でOKです

確認ボタンを押します。
登録ステータスが、「登録中」→「登録済み」になればOKです。

### Twilio側の設定

ここでは、Twilioを使ってSIP電話機への着信や発信について解説します。
発着信にはTwilioの電話番号（050番号など）が必要になりますので、予め準備しておいてください。

#### SIP電話機への着信設定

前述の通り、SIP電話機への着信については、予めTwilioで購入した番号（SIP電話機に割り当てる番号）のWebhookを使います。
今回はTwilio Binsを使ってみます。

- Twilioの管理コンソールを開き、デベロッパーセンターを選択
- TwiML Binsを選択し、＋アイコンを押して新しいBinsを生成する
- [FRIENDLY NAME]には適当な名前をいれる
- [TWIML]には、以下のようなTwiMLを記述する

```lang:TwiML
<Response>
<Dial>
    <Sip>sip:内線番号@SIPドメイン名.sip.twilio.com</Sip>
</Dial>
</Response>
```
※内線番号、SIPドメイン名は皆さんの情報に書き換えてください

- Binsを保存
- 管理コンソールの電話番号を開く
- 購入した電話番号を選択し、[A CALL COMES IN]の選択リストを「TwiML」にし、先程作成したTwiML Binsを選択
- 「保存」ボタンを押す

ここまで完成したら、試しに購入した電話番号に電話をかけてみます。
SIP電話機が着信し、発信者番号が正しく表示されていることを確認しましょう。

#### SIP電話機からの発信

SIP電話機からの発信については、前述の通り、TwilioからのWebhookを受け付けて、相手先電話番号を判定してから、TwiMLを返すプログラムが必要となります。
そのため、TwilioからWebhookを受け付けるサーバーと、TwiMLを返すためのプログラムの実行環境を別途用意してください（今回はこの部分は説明しません）。

サーバー側のサンプルコードは以下のとおりです（Node.js+Express）。

```lang:Node.js
'use strict'
const twilioLibrary = require('twilio');

app.post('/sipInvite', (request, response) => {
  let to = request.body.To || '';
  let from_ = request.body.From || '';
  let toNumber = to.indexOf("@") > 0 ? to.substring(4, to.indexOf("@")) : ''; // 宛先から電話番号を抽出
  let fromNumber = from_.indexOf("@") > 0 ? from_.substring(4, from_.indexOf("@")) : ''; // 発信者番号から電話番号を抽出
  let typeTo; // 0:PSTN宛、1:SIP宛
  let number;  // PSTN用宛先
  let sip;  // SIP用宛先
  let callerId; // 発信者情報
  // 宛先番号を判定し、先頭が0以外ならば内線宛、0なら外線宛に発信する
  if (toNumber.substring(0, 1)!=='0') {
    typeTo = 1;
    sip = 'sip:'+toNumber+'@<SIPドメイン>.sip.twilio.com';
    callerId = fromNumber;  // 発信者の内線番号をそのまま利用
  } else {  // PSTN宛
    typeTo = 0;
    number =  (toNumber.substring(0, 1) === '+' ? toNumber : '+81' + toNumber.substring(1));   // 0AB〜Jを+81に変換
    callerId = "<購入済み電話番号>"; // SIP電話機が複数ある場合は、その電話機用のTwilioの電話番号を検索してください
  }

  let resp = new twilioLibrary.TwimlResponse();
  resp.dial({
    callerId: callerId,
  }, function() {
    if (typeTo === 0) { // PSTN宛発信
      this.number(number);
    } else {  // SIP宛
      this.sip(sip);
    }
  });
  
  // TwiMLを返却
  response.type('text/xml');
  response.send(resp.toString());
});
```

この例では、TwilioからのWebhook（POSTメソッド）を受信し、Toフィールドの@より前の部分を判定することで、相手先がPSTNなのかSIPなのかを判定しています。判定基準は、相手先電話番号の先頭が0かどうかです。
また、このコードでは、発信者番号をスタティックに指定していますが、本来はFromフィールドを解析して、その内線番号に割り当てた外線番号（Twilioで購入した番号）を設定するようにします。
あとは、単純にTwiMLを返すだけですので、コードは複雑に見えますが、やっていることは非常にシンプルです。

サーバーの設定ができたらSIP電話機から電話をしてみましょう。

## 制限事項

現在、SIPレジストレーションには以下の制限事項があります。

10,000 Active registrations per SIP Domain
20,000 Active registrations per Account
10 Endpoint registrations per Address-of-Record (AOR) - Note that the 11th registration attempt for the same AOR will be rejected
10 REGISTER requests / second / AOR
※ AOR:SIPアドレス（例:alice@sip.domain.com）