この記事ではKDDIの電話バックエンドであるTwilioを用いて携帯電話や固定電話と通話できる「普通の電話」を作ります。
はっきり言って、これをやっている人は結構います。しかし、自分でやってみたところ予想外に苦戦してしまったので、どなたかの役に立てればと思い敢えて記事にすることにしました。あらかじめわかっていれば難しい話ではありません。
ただし、ここでご紹介する方法は、セキュリティを一切考慮していません。通信路の暗号化もやっておらず生パスワードが流れてしまいます。このまま実用に供することはできませんのでご了承ください。Twilio有料版ではより安全なTLS通信もできるのでご検討を。
なお、この記事はAkihiro KomoriとKei TakahashiとShigeru Owadaの合作です。
用語説明
Twilio
TwilioはKDDIが提供しているIP電話用バックエンドで、安価な使用料と使いやすいAPIで話題沸騰中です。国内のハッカソンなんかでもよく見かけますし、ハンズオンなども頻繁に開催されています。Mashup Awards #9の最優秀作品「1Click飲み」なんかは本当に素晴らしい利用例だと思います。
SIP
SIPとは、昔からあるIP電話用のプロトコルです。中身は割とHTTPっぽいです。通話データは通常UDPでやりとりします。
ネット上の音声や映像を介した通話に関して目下ホットな話題はWebRTCというもので、ブラウザから簡単に使えるようになっています。しかし、既存のIP電話網としてSIPベースのものは多々稼動しているそうですし、ブラウザレスで使いたい用途もあるので(私はこちらでした)、いまだSIPは実用的な選択肢と言ってよいと思います。SIPを用いて電話をかけたり受け取ったりするスマホアプリ(SIPクライアント)も多数存在しています。
SIPトランク
trunk はかばんのトランクと同じつづりですが、「幹」という意味から、電話の通信路を表します。
通常の電話のトランクは専用線ですが、「sip トランク」はこれをインターネット(IP網)で行う技術です。
Twilio のサーバと sipトランクで接続すると、Twilio は電話回線網(PSTN) にもつながっているので
電話発信ができるようになります。
IP-PBX
IP電話の電話交換機能を持ったサーバーです。本記事で用いるAsteriskをはじめ、FreePBXやFreeswitchなどいくつかの種類があります。
##ハマりどころまとめ
1. IP-PBXサーバーの必要性
SIPでTwilioにつないで音声通話しようと思ったら、現在ではTwilioだけでは完結できず、グローバルIPな場所にAsterisk等のIP-PBXサーバを立てる必要があります
※WebRTCでの通信やSMSだけならTwilioのみでもできます。The Twilio Node Helper Libraryをご参照のこと
TwilioとAsteriskとの連携については、英語版Twilioの公式情報もあります。しかし、この情報を頼りに作っていたら以下2点目の問題に気づけず試行錯誤が大変でした。米国内で実施すれば簡単なのかもしれませんが…
なお、Twilio×SIPに関しては、2016/9現在TwilioにSIP Registrationというサービスが準備されていて、これが使えるようになれば直接つなげる可能性もあります。現在は登録者にのみEarly accessを許可するというステージのようです。なお、私は二回ほどリクエストを送りましたが、一か月以上返事がありません。気長に待つことにします…
いずれにせよ今回はこの機能を使わず、Asteriskを仲介用に立てることにします。
2. 英語サイトで試すとハマる
Twilioのサービスは英語サイト(https://www.twilio.com/)と日本語サイト(http://twilio.kddi-web.com/)がありアカウントも完全に分かれています。それぞれ「地域による制限」という属性があり、無料トライアルアカウントの範囲内では「地域による制限」を変更することができないので、英語サイトで取得した電話番号では(日本の番号であっても)日本国内との通話ができません。日本で使うなら日本語サイトで電話番号を取得することが必要です。
3. 無料だと「検証済み番号」にしか電話をかけられない
Twilioのアカウントを作るときに、すでに持っている電話を使っての番号認証を要求されます。無料トライアルアカウントのうちはこの電話番号にしか電話をかけられません。ご参考
Asteriskサーバを立てる
さて、早速作業に移りましょう。
まず、SIPクライアント間(SIPクライアントアプリを入れたAndroidスマホ間の通話でチェックしています)での通話ができるようにします。間にAsteriskという仲介用のサーバーを立てます。閉じた同一ネットワーク内でのSIPクライアント間通信だけであれば、両クライアントからAsteriskに接続できればいいので、AsteriskサーバにグローバルIPアドレスが割り当てられている必要はありません。ノートPCでもラズパイでも、なんでもいけます。
ただ、今回は最終的にTwilioと連携して公衆電話回線に接続したいので、最初からグローバルIPがある所にサーバーを立てるつもりで説明します。このAsteriskが走っているサーバーのIPアドレスを以下ASTERISK_IPと呼ぶことにします。以下ASTERISK_IPという文字列を見かけたら、IPアドレスに読み替えてください。
以下の図のようになります。
インストール
AsteriskにはいろいろなOS向けのバージョンがありますが、Linuxの場合はRedHat系よりDebian系の方が断然おすすめです。なぜならyumにはAsteriskパッケージが入っていないのでソースからコンパイルしないといけないのですが、aptには最初から入っているからです。ここではUbuntu 16.04 64bitを用います。
コンソールからAsteriskをインストールします。
$ sudo apt-get install asterisk
設定ファイルを編集する
インストールすると自動的に実行が始まってしまいますが、デフォルトの設定になっているので自分用の設定を行います。
いじらないといけないのは/etc/asterisk/の下にあるsip.confとextensions.confの二つだけです。sip.confにはAsterisk全体の設定や、SIPクライアントから接続するときのアカウント情報を定義します。extensions.confには、電話がかかってきたときの挙動を記述します。
まずデフォルトのファイルを退避しておきます。
$ cd /etc/asterisk
$ sudo mv sip.conf sip.conf.bak
$ sudo mv extensions.conf extensions.conf.bak
そして、nanoエディタなどを用いて、/etc/asterisk/sip.confを新たに以下のように作成してください。
セミコロン(;)から行末まではコメントです。
[general]
context=default
port=5060
bindaddr=0.0.0.0
language=ja
;nat=yes
[user1]
type=friend
defaultuser=user1
secret=password1 ; 極力変更してください
host=dynamic
canreinvite=no
[user2]
type=friend
defaultuser=user2
secret=password2 ; 極力変更してください
host=dynamic
canreinvite=no
/etc/asterisk/extensions.confは以下のようにします。
[default]
exten => 1,1,Dial(SIP/user1,30,r)
same => n,Hangup()
exten => 2,1,Dial(SIP/user2,30,r)
same => n,Hangup()
新設定をAsteriskに反映させましょう。
$ sudo service asterisk restart
必要なポートを開く (環境次第)
Asteriskの実行にはデフォルトで5060番ポートと、10000-20000番ポートが開いていないといけないので必要に応じてポートをあけてください。10000番台全部なんて多すぎる!という場合は、/etc/asterisk/rtp.confで変更できます。
デフォルトでいい場合、ufwでポートを開けるコマンドはこうなります。
$ sudo ufw allow 5060
$ sudo ufw allow 10000:20000/udp
これでAsteriskの設定完了です。
CSipSimpleで内線通話を行う
Android端末を2つ用意して、SIPクライアント「CSipSimple」を両方にインストールします。(ほかのクライアントも使えると思いますがチェックしていません。以下CSipSimpleの「Basic」というウィザードを使う前提で解説します)
- 片方のSIPクライアントはユーザー名 user1 , パスワード password1 、SIPサーバー名 ASTERISK_IP と設定します。
- もう一方は user2 , パスワード password2 、SIPサーバー名 ASTERISK_IP と設定します。
それぞれ、「アカウント名」はCSipSimple内での区別に使うだけなので自由に決めていいです。
こうしておいて、user1を設定したほうから電話番号「2」をダイアルすると、もう一方の電話が鳴り、通話できるはずです。
逆に、user2を設定したほうから「1」をダイアルすれば、user1の方が鳴って、通話できるようになります。これだけでも割と感動しませんか?僕はしました。
うまくいかない場合
Asteriskが両SIPクライアントから普通に見えている場合はこれでうまくいくはずですが、AsteriskサーバーだけがNAT内にあるとき、つまりAsteriskサーバマシンにローカルIPアドレスと、外向けのIPアドレスがあるとき(Amazon EC2やNifty Cloudなどで走らせた場合がこれに相当します)には、sip.confの上の方にある
;nat=yes
の行の最初のセミコロン(;)を削除した上でservice asterisk restartしてみてください。
#Twilioの設定
さて、次は通常の電話回線での通話(外線通話)を可能にしたいので、Twilioへのアカウント登録とエラスティックSIPトランクの設定を行います。
最初にも書きましたが、国内から使うなら、必ず日本語のTwilioサイト(http://twilio.kddi-web.com/)で作業を行ってください。サイトには日本語と英語が入り混じっていますが気にしないことにしましょう。
以下多数のスクリーンショットが続きますが、これで全てではなくかいつまんで掲載しています。進めながら適宜判断してください。
アカウント作成
まずはSign Up for freeをクリックしてアカウントを作成します。SMSなどでアカウント作成者の確認を行うため、あらかじめなにがしかの電話番号が必要です。
電話番号取得
アカウントを作成した後に最初にやることは、電話番号の取得です。トップメニュー(ダッシュボード)から「#電話番号」という項目をクリックすると、電話番号を購入できます。
項目を進んでいくと、「電話番号を購入」というところがあります。トライアルアカウントでも1つなら無料で番号を使えるので、お金がなくても臆せずに進みます。(ずっと使っているとそのうちトライアル期間が終了になってしまいますが)
国を選んでから検索し、適当なものを選んで購入してください。デフォルトの日本の番号(+81からはじまるもの)を購入すればよいでしょう。北米およびカナダの番号であれば日本国内との通話は大丈夫だと聞きましたが試したことはありません。敢えて必要がなければ日本の番号が無難だと思います。
ここで手に入れた電話番号を、以下の説明ではMYPHONE_NUMBERと表現します。国内の電話番号を国番号から表現すると本来+81-XX-XXXX-XXXXのような形になりますが、この先頭の+81を除いた部分をMYPHONE_NUMBERとします。従って、国番号を省略して電話する場合は先頭に0をつけてその後にMYPHONE_NUMBERをプッシュすることになります。例えば、国内の番号で050-1234-5678であれば、国番号から書くと+81-50-1234-5678となり、この場合のMYPHONE_NUMBERは5012345678です(ハイフンは省きます)。以下MYPHONE_NUMBERという文字列を見たら、0抜きの電話番号に読み替えてください。
エラスティックSIPトランクを作成する
番号を購入したら、ひとまずトップに戻ります。左上の家のボタン状のタブですが、実際に押せるのは家そのものではなく、家のアイコンのちょっと下あたりの狭い領域だけなので注意してください。
戻ったら今度は「エラスティックSIPトランク」を押して、好きな名前でトランクを作ります。
ここではzphoneという名前のものを作ります。
作成すると次の画面になります。
認証情報を作成する
左にTerminationとかOriginationとかの設定項目があり設定しないといけないのですが、ここにも小さな落とし穴があります。Terminationの設定をする前に、「戻る」で一度上の階層に行き、「認証」から「IPアクセスコントロールリスト」と「クレデンシャルリスト」を作成しておく必要があるのです。なぜかというと、「IPアクセスコントロールリスト」はここでも作れるのですが、「クレデンシャルリスト」はGUIの見た目が全く一緒にもかかわらずここでは作れませんので、とりあえずどっちも作れる上の階層で両方あらかじめ作っておきましょう。
「IPアクセスコントロールリスト」では、フレンドリーネーム(任意)と、接続元となる ASTERISK_IP を入力します。なぜかフレンドリーネームを入力するところが二つありますが、同じでOKでした。私はzaclという名前にしました。
実はこのIPアドレス制限は必須ではありませんが、他人につながれても何もいいことはないどころか乱用される恐れもあるので設定しておいた方が無難です。
「クレデンシャルリスト」では、AsteriskがTwilioに接続するときのユーザー名とパスワードを設定します。こちらにも任意のフレンドリーネームがつけられます。ここではzcredという名前にしました。
Termination設定を行う
認証情報を作り終えたら、メニュー内の「トランク」から先程作ったトランクを選び、今度こそTerminationの設定を行います。
Termination とは、一般に発信された電話を受ける端末のことを指します。ここでは、なにがしか外部のシステムがTwilioを使って電話をかけたいときに接続するTwilio側の受け口ということになります。
まずTERMINATION SIP URIというものを作成します。ここはグローバルな、pstn.twilio.comのサブドメインを作ることになるので、既存の名前と衝突しない名前を付ける必要があります。衝突していなければ「Available」と表示されます。
加えて、「認証」のところに先程作った「IPアクセスコントロールリスト」と「クレデンシャルリスト」を設定します。(右側の+マークを押さなくても、テキストエリアをクリックすると選択肢が出ます)
設定したら、忘れずに「保存」を押してください。
Origination設定を行う
次に、Originationを設定します。ここでのOriginationとは、Twilio側から見れば、Twilioが電話網から受け取った電話を外部のシステムに投げるときの設定になります。
「Add new Origination URI」というボタンを押し、最初の「オリジネーションSIP URI」のところに、
sip:ASTERISK_IP
と書けばOKです。(ASTERISK_IP は正しいIPアドレスに置き換えてください)
ここでも「追加」してから「保存」を忘れずに押してください。
電話番号のセット
先程取得した電話番号が表示されているので、右側のボックスにチェックを入れたうえで「Add Selected」を押します。
これでTwilio側はオッケーです。作成した電話番号と、sipトランクのURI、そしてクレデンシャル情報(ユーザー名とパスワード)を次の設定で用います。
Asterisk設定ファイルにTwilio関連情報を追記する
さて、次が我々が悩んで仕方なかった最後の関門、AsteriskとTwilioが連携するようにAsteriskの設定ファイル2つ(sip.confとextensions.conf)の書き換えです。
sip.confのほうは、[general]の項目の最後とファイルの最後にTwilio用の情報を書き加えます。
[general]
context=default
port=5060
bindaddr=0.0.0.0
language=ja
;nat=yes
srvlookup=yes
udpenable=yes
tcpenable=yes
preferred_codec_only=yes
disallow=all
allow=ulaw ;Twilio does G.711 only
externip=[ASTERISK_IP] ; Asteriskサーバーの外向きIPアドレス。角カッコ必要。(恐らくnat=yesのときのみ必須)
[user1]
type=friend
defaultuser=user1
secret=password1
host=dynamic
canreinvite=no
[user2]
type=friend
defaultuser=user2
secret=password2
host=dynamic
canreinvite=no
[twilio-trunk](!)
type=peer
dtmfmode=rfc4733
canreinivite=no
insecure=port,invite
[twilio0](twilio-trunk)
host=zphone.pstn.twilio.com ; SIP TrunkのTermination URI
defaultuser=CREDENTIAL_USER_NAME ;Twilioの「クレデンシャルリスト」で設定したユーザー名
remotesecret=CREDENTIAL_PASSWORD ;Twilioの「クレデンシャルリスト」で設定したパスワード
[twilio1](twilio-trunk)
host=54.65.63.192
[twilio2](twilio-trunk)
host=54.65.63.193
[twilio3](twilio-trunk)
host=54.65.63.194
[twilio4](twilio-trunk)
host=54.65.63.195
4か所ほど、自分の情報に書き換えなければいけないところがあります(コメントが入っている行)。
[twilio1]~[twilio4]に書かれているIPアドレスは、日本から電話がかかってきたときにはじかないようにするための、Twilioの持つサーバーIPアドレスですので変更の必要はありません。
extensions.confも以下のように書き換えます。
[default]
exten => _1,1,Dial(SIP/user1,30,r)
same => n,Hangup()
exten => _2,1,Dial(SIP/user2,30,r)
same => n,Hangup()
exten => _0.,1,Set(CALLERID(all)="Mr Twilio" <+81MYPHONE_NUMBER>)
same => n,Dial(SIP/twilio0/+81${EXTEN:1})
exten => _+81MYPHONE_NUMBER,1,Dial(SIP/user1,30,r)
same => n,Hangup()
この中で、MYPHONE_NUMBERとなっているところは、前述のようにTwilioで取得した電話番号の一番上の桁の0を除いたものに置き換えてください。2か所あります。スペースを開けたりハイフンを入れたりしてはいけません。
ちょっと解説すると、新しいextensions.confでは、下に2セット項目が増えています(合計4行)。最初のは、0から始まる番号をプッシュされた時の挙動で、0を+81に置き換えて電話網にダイアルすることになります。
なお、「ハマりどころまとめ」で説明したように、無料トライアルアカウントの場合は、Twilio登録時に指定した電話番号にしかかけられないので注意してください。
最後の項目は、Twilioで取得した番号に電話がかかってきたら、user1のSIPクライアントに転送するという設定になります。
こうしておいて、Asteriskを再起動して変更を反映します。
$ sudo service asterisk restart
完成!
これで全部できあがりです。
先程つないだ2台のAndroid用SIPクライアントのどちらかでログインし、080...のようにアカウント作成時に登録した番号に電話をかけてみてください。話ができるはずです。有料アカウントにアップグレードすれば、任意の電話番号にかけられます。
また、お手持ちの電話からTwilioで取得した番号、すなわちMYPHONE_NUMBERの先頭に0を補ったものに電話をかけると、今度はuser1のSIP Clientに電話をかけることができます。国番号から入れてはいけません(+81から入れてはいけない)。かならず0から始まる電話番号にしてください。
最初に作った内線の設定も生き残っているので、user1の方から2を、またはuser2の方から1を、どちらか押すと、Twilioを介さず無料内線通話ができます。AsteriskサーバーはグローバルIPを持っていますので、宅外からでも無料で内線電話ができるというわけです。
#残った課題
外線からSIPクライアントに電話をかけるとき、呼び出し音は流れているのにSIPクライアント側に着信しないことが時々あります。原因不明です。
そういえば家の商用IP電話でもたまにそういうことがあるので、信頼性はその程度なのかも…いやそんなはずはないか。よくわかりません。
#まとめ
最後だからもう一度だけ言わせてください。結果を見れば割とシンプルな設定に見えますが、ここにたどり着くまでにはかなりの苦労がありました。TwilioのエラスティックSIPトランクは一筋縄でいかないところがあるなあと思った次第です。
でも、普通の電話もプログラムで制御できるというのは大変興味深く、何か新しいものが生み出せるのではとワクワクしますね。みんなでいろいろ考えて、Twilioを盛り上げていきましょう!
#Twilioサポートチームの神対応について
Twilioのお試し版を抜け有料版に移行するには、最初に米国サイトでは$20、日本サイトでは2000円の前払いが必要です。前述のように日米のサイトは完全に分かれて別サービスになっていますが米国サイトでは目的が達せないことが分かったので、米国サイトのクレジット残額をなんとかしてほしいとTwilioにクレームを出したところ、すんなりとクレジットカードに残額を戻してくれました。この対応には驚きました。素晴らしいですね。
KDDIさん、ありがとうございました!