coturn使ってみた


使ってみたいと思いつつも使っていなかったturnserverを使ってみたのでメモします。

#環境
さくらクラウド CentOS release 6.5 (Final)
サーバー [coturn](https://code.google.com/p/coturn/)

#coturnのビルド
自分の環境では***/user/local/src/***にソースを用意しました。
最初に***configure***した際に***libevent2***も必要と言われたので[ここ](http://libevent.org)から取得

```bash
> pwd;ls -F
/usr/local/src
libevent-2.0.21-stable/  turnserver-4.3.1.2/
```

まず***libevent2***で

```bash
./configure
make
make install
```

***coturn***も同様に

```bash
./configure
make
make install
```

何も指定せずにビルドすると、

```bash
> which turnserver
/usr/local/bin/turnserver
```

***/usr/local/bin***に実行ファイルが作成される。
***conf***ファイルは***/usr/local/etc***に作成されます。
テンプレートができますので、それをコピーして修正

```bash
> pwd;ls 
/usr/local/etc
turnserver.conf  turnserver.conf.default
```

基本はデフォルトですが、編集した項目が

```bash
#グローバルIP
listening-ip=XXX.XXX.XXX.XXX
#localhost指定
relay-ip=127.0.0.1
min-port=49152
max-port=65535
verbose #ログの詳細がみたいので解除
no-tls
no-dtls
stun-only
```

とりあえずこれだけを指定
#動作検証

```bash
# turnserver
0: log file opened: /var/log/turn_3786_2014-12-09.log
0: 
RFC 3489/5389/5766/5780/6062/6156 STUN/TURN Server
Version Coturn-4.3.1.2 'Tolomei'
0: 
Max number of open files/sockets allowed for this process: 4096
0: 
Due to the open files/sockets limitation,
max supported number of TURN Sessions possible is: 2000 (approximately)
0: 

==== Show him the instruments, Practical Frost: ====

0: TLS supported
0: DTLS supported
0: AEAD supported
0: SQLite is not supported
0: Redis is not supported
0: PostgreSQL is not supported
0: MySQL supported
0: MongoDB is not supported
0: OpenSSL compile-time version 0x1000105f: fresh enough
0: Default Net Engine version: 3 (UDP thread per CPU core)

=====================================================

0: Config file found: /usr/local/etc/turnserver.conf
0: Listener address to use: XXX.XXX.XXX.XXX
0: Relay address to use: 127.0.0.1
0: Config file found: /usr/local/etc/turnserver.conf
0: Domain name: 
0: Default realm: 
0: pid file created: /var/run/turnserver.pid
0: IO method (main listener thread): epoll (with changelist)
0: WARNING: I cannot support STUN CHANGE_REQUEST functionality because only one IP address is provided
0: Wait for relay ports initialization...
0:   relay 127.0.0.1 initialization...
0:   relay 127.0.0.1 initialization done
0: Relay ports initialization done
0: IO method (general relay thread): epoll (with changelist)
0: turn server id=0 created
0: IPv4. TCP listener opened on : XXX.XXX.XXX.XXX:3478
0: IPv4. UDP listener opened on: XXX.XXX.XXX.XXX:3478
0: Total UDP servers: 0
0: Total General servers: 1
0: IO method (cli thread): epoll (with changelist)
0: IPv4. CLI listener opened on : 127.0.0.1:5766
0: IO method (auth thread): epoll (with changelist)
```

```bash
> netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State      
tcp        0      0 XXX.XXX.XXX.XXX:stun        *:*                         LISTEN      
udp        0      0 XXX.XXX.XXX.XXX:stun        *:*                                     
```

stun(3478)にtcp,udpでサーバーが起動しています。

今回はただの動作確認なので、クライアントを[ここ](https://github.com/node/turn-client/blob/master/py-stun-client-demo.py)から持ってきたんですが、stunヘッダーが少し違っていたので書き換え。

```py
# default configuration 
host = argv[1]  #"グローバルIP"  
port = 3478
local_ip = '自分のローカルIP(192.X.X.X?)'
local_port = 7878
```

```py
## first bind 
def stun_binding_first(sock):
    print '\nSTUN binding request ...'
    # header
    header = struct.pack('!H',STUN_method['STUN_METHOD_BINDING']) # binding req
    header += struct.pack('!H',0x00)               # length
    header += struct.pack('!l',STUN_MAGIC_COOKIE)    # magic cookie
    header += struct.pack('!12s',genTranID())        # trans ID
    # body 
    body = struct.pack('!H',0x8022)           # software   
    body += struct.pack('!h',0x0014)          # length
    body += struct.pack('!20s','PY STUN CLIENT 0.1')#value
    # msg
    send_data = header #+ body # TODO 
    
    
    print 'Send data ...'
    r = sock.sendto(send_data,(host,port))
    print 'size=',r
        
    print 'Recv data ...'
    buf,address = sock.recvfrom(1024)
    print 'size=',len(buf)
    
    print 'data: ',buf
    print 'from: ',address
    
    (header,attr) = recv_resp(buf)
    
    # TODD: parse: realm, nonce 
    print ''
    return (header,attr)
```

これで動作確認

```bash
#クライアント
$>python stuncli.py XXX.XXX.XXX.XXX
STUN(RFC5389) client demo by Chris <nodexy@gmail>

Connect to server  :  XXX.XXX.XXX.XXX 3478
Bind local ip:port :  192.xxx.xxx.xxx 7878

STUN binding request ...
Send data ...
size= 20
Recv data ...
size= 84
data:#省略しました
????ɀ"Coturn-4.3.1.2 'Tolomei'?r??΀+
from:  ('XXX.XXX.XXX.XXX', 3478)

>>header: 
0x101 64 2112a442 #省略しました
>>attrs:
32 XXX.XXX.XXX.XXX : 7878  (XOR= #省略しました ) #ここがNAT以降のグローバルIPのはず

>>> Success! XOR-MAPPED-ADDRESS= XXX.XXX.XXX.XXX:7878
END!
$>
```

```bash
#サーバー
0: Total UDP servers: 0
0: Total General servers: 1
0: IO method (cli thread): epoll (with changelist)
0: IPv4. CLI listener opened on : 127.0.0.1:5766
0: IO method (auth thread): epoll (with changelist)
730: handle_udp_packet: New UDP endpoint: local addr XXX.XXX.XXX.XXX:3478, remote addr XXX.XXX.XXX.XXX:7878
1
730: session 000000000000000001: realm <> user <>: incoming packet BINDING processed, success
733: session 000000000000000001: closed (2nd stage), user <> realm <> origin <>, local XXX.XXX.XXX.XXX:3478, remote XXX.XXX.XXX.XXX:7878, reason: allocation watchdog determined stale session state
```

サーバーは２回目のリクエストがこないのでタイムアウトしています。
ただ、クライアントには(多分)NATを通った後のIPがサーバーから返却されていることが確認できました。
まだ、あまりよくわかっていませんので凝ったことは何もできていません。
とりあえず動いたかな程度ですね。
環境によってはサーバーから何も返却されないこともあるかもしれません。
とりあえず動いたのでメモしました。