やったことだけ書く備忘録

WebRTCを仕組みから実装までやってみる



このエントリはFrontrend Advent Calendar 2013 23日目の記事です。



2014/03/16追記
WebRTC-DataChannelについてもエントリ書きました。↓からどうぞ。
WebRTC-DataChannel使ってみたよ



WebRTCを仕組みの理解から実装まで


Advent Calendarを書くということでなんか新しいことやったほうがいいかなーって思ってたので、今回はWebRTCを調べてみました。
調べながらだったので間違っている箇所もあるかもですが、専門家の方のツッコミあれば歓迎です。

先に作ったサンプルデモを触りたい方は以下のアドレスからどうぞ。

WebRTC Video Chat Sample

※接続名は同時にアクセスしている方全員から見えますのでご注意ください!接続依頼が来た際にはダイアログが出るようにしてますが、安易に応答すると知らない人とつながっちゃうので、知人同士など見知った方同士でやることを強くオススメします。

ソースコードはいつもどおりGithubにあげてます。

ysugimoto / RTCPeerConnectionSample


概論



そもそもWebRTCって何?って話ですが、




WebRTC (Web Real-Time Communication)とはWorld Wide Web Consortium (W3C)が提唱するリアルタイムコミュニケーション用のAPIの定義で、プラグイン無しでウェブブラウザ間のボイスチャット、ビデオチャット、ファイル共有ができる。


WebRTC: Wikipediaより引用




W3C: WebRTC 1.0: Real-time Communication Between Browsers



ということです。もう少し突っ込むと、ブラウザ間でそれぞれ「peer(ピア)」と呼ばれるオブジェクトを保持し、その異なるピア間でデータ通信(ストリームを含む)を行う、という感じですかね。
重要なのは、そのピア同士の通信はサーバを介さない、ということでしょうか。それぞれのクライアントが直接通信を行うので、サーバーをTCPなどで介してやりとりするよりも手軽でコストも低い感じですね(実装は手軽じゃありませんが)。



最近では「SkyWay」というサービスもリリースされましたね。こちらはpeer.jsというPeer接続をうまくラップしているライブラリを使って実現しているようです。



ちなみに本エントリではWebRTC仕組みの理解とChromeに実装されている素のAPIを介して通信をするのが目的なのでライブラリは使いません。SkyWayでうまく通信できなかったのが悔しかったんじゃないんだからね
普段ライブラリ使ってる方も根本の仕組みの確認を含めて参考にしていただければ幸いです。



なお、Chromeで動かす場合はchrome://flagsで設定を一部有効化しておく必要があります。webRTC周りとgetUserMedia周りの設定を有効にした状態で望みましょう。



WebRTCを実現する上で必要なステップはおおまかに以下です。




  • Peerの生成

  • メディア接続

  • Call対象の特定

  • Sessionの共有

  • メディアストリームの共有



あと、知っておくべきキーワードも上げておきます。知らないとダメってわけでは無いですが、本格的なものを作るとなると必須かと。




  • Candidate

  • (Remote/Local)Description

  • ICE

  • NAT Traversal



なお、このあたりはWebRTCに使われるP2Pの技術というエントリですでにわかりやすく紹介されているので、私は主に実装よりの話にしようかと思います…
きっと一部の方に需要があると信じています。



Peerの生成


WebRTCのAPIは現在はChromeとFirefoxで実装されています。どちらもwebkitRTCPeerConnection/mozRTCPeerConnectionのベンダープレフィックス付き実装です。
後述するRTCIceCandidateとRTCSessionDescriptionはベンダープレフィックスがついていないので注意が必要です。ついてない理由はおそらく単純に文字列をラップした型付きオブジェクトであればいいからで、
ロジカルなメソッドは実装される必要はないからではないかと思われます。でもややこしい。




var peer = new webkitPeerConnection({
    
"iceServers": [{"url""stun:stun.l.google.com:19302"}]
});
 


引数にはiceServerと呼ばれる情報を持ったオブジェクトを渡します。ここは正しい引数形式で渡さないと例外となるので注意。
iceServersというキーに対してurlという文字列データを持ったオブジェクトの配列をセットしますが、面倒なので上のコードのようなフォーマットで渡せばOKです。
"stun:stun.l.google.com:19302"というのはGoogleが提供しているSTUNサーバを使わせてもらう指定です。STUNサーバについては後述します。

これでブラウザ上にpeerオブジェクトが生成されます。



メディア接続


HTML上に配置したvideo要素にカメラをattachします。おなじみnavigator.getUserMedia()ですね。こちらもwebkitGetUserMedia/mozGetUserMediaとベンダープレフィックス付きです。




navigator
.webkitGetuserMedia(
    { 
audiotruevideotrue },
    
successCallback,
    
errorCallback
);
 


第一引数にはメディアソースに関するオブジェクトを渡します。audio/videoともにtrueなので、カメラとマイク接続を指定する指定です。なお、接続には許可を求めるダイアログバーが出るので、そこから許可を選択します。
第二引数には成功時、第三引数には失敗時のコールバック関数を登録します。例えば、成功時にvideoに接続するには以下のように。




navigator
.webkitGetUserMedia(
    { 
audiotruevideotrue },
    function(
stream) {
        
// video要素取得
        
var video document.getElementById('video');

        
// srcにBlob URLを指定するとカメラの画像がストリームで流れる
        
video.src window.webkitURL.createObjectURL(stream);

        
// 自分のpeerにカメラストリームを接続させる
        
peer.addStream(stream)
    },
    function(
err) {
        
console.log(err.name ': ' err.message);
    }
);
 


peer.addStream(stream)とすると、RTC接続確立後に相手のストリームに渡されるようになります。マイク入力はvideo要素から流れます。



Call対象の特定


W3Cには「SignalingChannel」というオブジェクトを使っているようですが、HTML5 Rocksによれば、この部分はWebRTCとは別みたいなことが書かれていて、WebSocketやSIPとか使えるよって書いてあります。
一番簡単そうなのでWebSocketでやるのがいいですね。SIPだったらAsteriskとか使うといいのかも。で、特定にはUUIDなどをつけて管理することで特定の相手に向けてコールしたりできるようにしています。
後述しますが、RTCIceCandidateやRTCSessionDescriptionのデータ自体はJSONシリアライズ可能なデータなので、WebSocketでも十分に転送可能です。



Sessionの共有


コール対象が特定できるようになったところで、実際にコールしてPeer接続を行うには、RTCSessionDescriptionと呼ばれるセッションの共有が必要です。ここがややこしいところでした…。
大事なのは、「localとremoteそれぞれのDescriptionをクロス接続させる」ということだと理解しています。わかりにくいのでAliceとBobの接続フローで説明してみます。
なお、RTCでは接続依頼を「Offer」、返答を「Answer」というみたいです。




  1. Alice: BobにOfferを送る

  2. Peer: AliceのOfferリクエストからDescriptionを生成してAliceに渡す

  3. Alice: 生成されたDescriptionをLocalDescriptionにセット

  4. Peer: DescriptionをBobに転送

  5. Bob: 受け取ったDescriptionをremoteDescriptionにセットする

  6. Bob: AliceにAnswerを送る

  7. Peer: AnswerリクエストからDescriptionを生成してBobに渡す

  8. Alice: 生成されたDescriptionをLocalDescriptionにセット

  9. Bob: 受け取ったDescriptionをremoteDescriptionにセットする

  10. Peer: DescriptionをAliceに転送

  11. Alice: 受け取ったDescriptionをremoteDescriptionにセットする

  12. 接続確立(∩´∀`)∩



うまく言葉で説明できない…ので、接続確立後の構成はこんなイメージになります。それぞれのlocal/remoteのDescriptionが相手のremote/localのDescriptionになっている感じです。





なお、SessionDescriptionのデータ本体も文字列データなので、WebSocketなどで転送可能です。ちなみに下記のようなデータです:




v
=0
o
=- 3883943731 1 IN IP4 127.0.0.1
s
=
t=0 0
a
=group:BUNDLE audio video
m
=audio 1 RTP/SAVPF 103 104 0 8 106 105 13 126

// ...

a=ssrc:2223794119 label:H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh810
 


詳しくはSession Description ProtocolのWikipediaを参照してください。



Offserの送信


まずOfferを送ります。このタイミングで自分用(local)のSDPができますので、コールバック内でセットし、そのSDPを相手に送ります。



2013/12/23追記: SDPインスタンスのtypeプロパティからOffer/Answerの判定ができる、との指摘を頂きました。
それに基づいてwebsocketのメッセージハンドラ内の処理と送信パラメータを変更しました。ありがとうございます。




// Offer送信
peer.createOffer(function(sdp) {
    
// 引数のSDPは自分用
    
peer.setLocalDescription(sdp, function() {
        
// セット完了したら、相手に自分のSDPを送る
        
websokcet.send(JSON.stringify({
            
"sdp"sdp,     // これ、自分の。
            
"to" to_uuid  // 君に届け
        
}));
    });
});

// websocketのメッセージイベントで受け取る
websocket.onmessage = function(evt) {
    var 
message JSON.parse(evt.data),
        
sdp;

    if ( 
message.sdp ) {
        
sdp = new RTCSessionDescription(message.sdp);
        
// 相手用(remote)にセット
        
peer.setRemoteDescription(sdp, function() {
            
// 自分へのOffer-SDPだったらAnswerを返す
            
if ( sdp.type === "offer" ) {
                
// ファイナルアンサー
            
}
        });
    }
};
 


ここでも自分用なのか相手用なのかをしっかり把握しておかないとハマります…。 特に同じソースで共有するので、切り分けをちゃんとする必要がありますね。現時点での状態は下のような感じです。





現在片想い中ですね。ちなみにここで認証作業などをすれば応答制御が可能だと思います。



Answerの送信


Offerを受け取ってremoteDescriptionにセットしたら、今度は送り元に対してAnswerを送ります。やることはOfferと同じですが、typeがAnswerになります。



// Answer送信
peer.createAnswer(function(sdp) {
    
// この引数のSDPは自分用!
    
peer.setLocalDescription(sdp, function() {
        
// セット完了したら、相手に自分のAnswerSDPを送る
        
websokcet.send(JSON.stringify({
            
"sdp"sdp,          // これ、自分の。
            
"to" from_uuid  // 君に届け
        
}));
    });
});
 


Answerを受け取ったら、同じくremoteDescriptionにセットします。これで晴れてクロスな接続が完了しました!

これでSessionの確立ができたので、ストリームの共有ができるようになるのですが、Offer/Answerの間に、数回ストリームの共有に関するCandidateと呼ばれるデータの受け渡しが発生します。
これは非同期なので、peer.onicecandidateというイベントハンドラ内で監視します。こちらも自分と相手双方で共有する必要があります。




peer
.onicecandidate = function(evt) {
    var 
candidate;

    
// evt.candidateプロパティにデータが入っているので、WebSocketでデータを共有するため送信
    
if ( evt.candiate ) {
        
websocket.send(JSON.stringify({"candidate"evt.candidate}));
    }
};

// websokcetのメッセージハンドラ内で送信されてきたデータを復元してセットする
websocket.onmessage = function(evt) {
    var 
message JSON.parse(evt.data),
        
candidate;

    
// evt.candidateがあればCandidateの共有
    
if ( evt.candidate ) {
        
// candidateってなんか甘そうだよね
        
candidate = new RTCIceCandidate(evt.candidate);
        
peer.addIceCandidate(candidate);
    }
    };
 


なお、このcandidateデータも下のようなフォーマットで文字列シリアライズ可能なオブジェクトなので、WebSocketで送信できます。




{
  
"sdpMLineIndex":0,
  
"sdpMid":"audio",
  
"candidate":"a=candidate:3802297132 1 udp 2113937151 192.168.0.3 54130 typ host generation 0\r\n"
}
 


この共有が終わると、今度はメディアストリームの共有に入ります。ストリーム共有の際もやはり非同期なので、peer.onaddstreamというイベントハンドラで監視します。




peer
.onaddstream = function(stream) {
    
// 自分のリモートにセット
    
var video document.getElementById('remoteVideo');

    
video.src window.webkitURL.createObjectURL(stream);
};
 


これでお互いのpeer間でビデオの共有が開始されます。手間がかかります。

ここで注意点は、RTCPeerConnectionオブジェクトには、peer.onaddstreamに対してpeer.addStream、peer.onicecandidateに対してpeer.addIcceCandidateというメソッドがありますが、これらは相互に作用するものではないという点に注意が必要です。
つまり、peer.addStream()をしたらpeer.onaddstreamが呼ばれそうなものですが、実際はそうではないんですねこれが。これに気づくのに1時間くらい無駄にしました。なので、

addStream()/addIceCandidate()メソッドは自分(local)用、
onaddstream/onicecandidateイベントハンドラは相手(remote)からの接続リクエストに対して、

というように分けて考えるとわかりやすいです。先に書いた接続のフローをもう一度まとめると、




  1. Alice: BobにOfferを送る

  2. Peer: AliceのOfferリクエストからDescriptionを生成してAliceに渡す

  3. Alice: 生成されたDescriptionをLocalDescriptionにセット

  4. Peer: DescriptionをBobに転送

  5. Bob: 受け取ったDescriptionをremoteDescriptionにセットする

  6. Bob: AliceにAnswerを送る

  7. Peer: AnswerリクエストからDescriptionを生成してBobに渡す

  8. Alice: 生成されたDescriptionをLocalDescriptionにセット

  9. Bob: 受け取ったDescriptionをremoteDescriptionにセットする

  10. Peer: DescriptionをAliceに転送

  11. Peer: candidateデータを双方に送信

  12. Alice/Bob: peer.onicecandidateイベント発火、candidateデータを相手に送信

  13. Alice/Bob: 受け取ったcandidateデータをpeer.addIceCandidate()メソッドでセットする

  14. Peer: streamデータを双方に送信

  15. Alice/Bob: peer.onaddstreamイベント発火、リモートのvideoにセットする

  16. 接続確立(∩´∀`)∩



11〜15の処理は非同期なので正確にどのフローで起きるかまではトレースできませんでしたが、だいたいこんな感じで接続が確立されると思います。

上記のフローを素のAPIで実装してみているので、興味があればgithubを見てみてくださいね。おまけでWebSocketのチャット機能もついてます。



まとめ


めちゃめちゃ面倒くさいけど、今の時点でも十分に簡単になっている感触はある。それでもpeer.jsとか使ったほうが楽でいいと思います。
どこで見たのか忘れてしまったけど、SkypeはVoIPなので、これとは違う技術でやってるらしいですね。

それから、RTCPeerConnectionオブジェクトには、他にもデータ送信に関するcreateDataChannel()メソッド、ダイアルトーンに関するcreateDTMFSender()メソッドがあります。
createDataChannelはChrome26から実装されている、と書かれていますがCanaryでないと例外を吐いて動きませんでしたので、また今度試す。createDTMFSenderは…時間があれば。

その他、再度参考サイトの紹介で終わりにしたいと思います。


WebRTC 1.0: Real-time Communication Between Browsers


WebRTC in the real world: STUN, TURN and signaling


WebRTC for Beginners


WebRTCに使われるP2Pの技術




現場からは以上…ですが、せっかくなのでNAT Traversalについても少し書いておきます。
あんまり関係ない話なので飛ばして頂いて構いません><








おまけ:NAT Traversalについて


いわゆるP2PにおいてNAT越えはとても重要な課題で、オンラインゲームの技術者さん方もNAT越えの技術研究をされているようです(ググるとPDF資料がたくさんヒットします)ね。
私はネットワークにそれほど詳しくないので調べたことだけを備忘録としてまとめておきます。このあたりの専門家の方に話を聞いてみたいですね。



そもそもの問題


通常はルータなどを介してインターネットに接続しますが、ルータにグロ−バルIPが割り当てられていて、それぞれのマシンは内部でプライベートアドレスとポートにマッピングされているのは一般的です。
WebRTCではその仕組みから「マシン同士が直接データ共有を行う」ので、それぞれのマシンのIPとポートを正確に、つまりNATによってマッピングされたあとのIPとポートまでを知っている必要があります。
ですが、そのマシン同士の経路にNATがあると、それぞれのIPアドレスとポートは通常読み取ることはできず、正確に知ることはできません。マッピングテーブルが変われば変わることだってあるでしょうし。
その問題を解決する方法が現在いくつかあるようです。

UPnP


「Universal Plug and Play 」の略称で、これを使ってポートマッピングの設定できるので、グローバスIPの取得が比較的簡単になる…らしい(よく調べてない)
モデムがUPnPに対応してないとダメらしいので、あんまり詳しく調べてない。

参考:UPnPのNAT越えについて調べてみた



STUN


「Simple Traversal of UDP through NATs」の略称で、NAT越えの方法としてRFC3489で定められた標準的な仕組み、とのこと。googleやMozillaもWebRTC用(かどうかはわかりませんが)にSTUNサーバが提供されています。

google: stun.l.google.com:19302
Mozilla: stun.services.mozilla.com
SkyWay: stun.skyway.io:3478

が使えそうです。冒頭で書いたSkyWayも独自にSTUNサーバを用意してくれているようです。素晴らしいですね。
外部のSTUNサーバに対してクライアントが一度接続し、グローバルIPとマッピングされたポート番号を記憶しておくことで、そのデータを使ってピアは相手のマシンを特定することができる、という感じかな?

ただし、STUNプロトコルでは取得したアドレスはすべてのピアに対して有効でない場合があり、
特にシンメトリックNATではNAT越えができないなどで、後述のTURNを併用されることもあるそうです。



TURN


STUNでは解決の難しかった問題を解決するためにTURNを併用することが多いそうです。TURNはほぼすべてのNATに対してピア接続を有効にする手段とのことですが、
P2Pの接続を中継することで実現するため、接続ごとにTURNサーバへの接続が必要であり、UDP/TCPのコストが発生するためサーバ提供側に対して非常に負荷がかかる。
そのため、STUNが使えるNAT環境ならSTUNを、ダメなら最終手段としてTURNで解決する手法がいいとのこと(Wikipediaより)。




STUN/TURNサーバについては、Google CodeにOSSがあるので一度手元のVPSにインストールして設置してみましたがうまく行かず・・・。
もう少し設定とネットワーク周りの調査が必要っぽいです。このへんは解決できたらエントリにするかもです。プライベートIPとグローバルIPの設定が必要らしいので、AWSならうまくいくかも。



で、これらの最適なNAT Traversalの方法を達成する方法を総称してICEと呼ぶそうです。RTCPeerConenctionの引数に「iceServers」と指定するのはこれのためでしょうね。配列で複数サーバ情報を渡せますし。
簡単なP2Pアプリ(外部にデータが漏れても良いような)ならGoogleやMozillaの提供するSTUNサーバで十分だと思います。

という、とりとめのないおまけでした。


今度こそ現場からは以上です。

« 前の記事 次の記事 »

132件のコメント

小松 さん

skywayスタッフの小松です。うまく通信できなかったとのこと失礼しました。解析したいため、宜しければskywayを試された時のコード頂けますでしょうか?kensaku.komatsu@gmail.comまで頂けますと幸いです。

sugimoto さん

ご丁寧にありがとうございます。
おそらくサービス側ではなく、ライブラリとブラウザ実装の問題だと思っておりますが、
念のため後ほどご連絡させていただきます。

Zarcharia さん

Going to put this arlitce to good use now.

Lenardkt さん

is fish oil pills good for you <a href=http://mkd.at/13/cialis.html>cialis generika</a> tick pills for dogs

Georgetono さん

mercy regional medical center lorain <a href= http://zolpidem.asso-web.com/ >zolpidem online</a> orthopedic doctors in austin tx

Jeffreybug さん

Howdy! <a href=http://nolvadex.xyz/>buy nolvadex online</a> great internet site.

MathewDror さん

Hello! <a href=http://finasteride365.com/>finasteride</a> beneficial internet site.

RobertLot さん

Hello there! <a href=http://cialis20mg-buy.com/>order cialis</a> , <a href=http://levitra-365.com/>levitra</a> , <a href=http://bestonlinepharmacy365.com/>safe online pharmacies</a> , <a href=http://cytotec-365.com/>buy cytotec</a> good site.

RobertBoli さん

Hi! [url=http://viagracheap.space/#buy-viagra-online-without-prescription]buy viagra medication[/url] very good site.

Michaelnes さん

Hi! <a href=http://5mgcialis.top/>buy cialis cheap</a> , <a href=http://doxycycline.online/>buy doxycyline online</a> , <a href=http://finasteride365.com/>where buy propecia</a> , <a href=http://sildalis.tech/>sildalis 100mg</a> very good internet site.

FrancisJarf さん

Hi! <a href=http://viagraonlinecanadianpharmacy.site/#purchase-viagra>pharmacy technician online course</a> great web page.

TimothyMarm さん

Hello! <a href=http://imitrex.club/>purchase imitrex</a> great web page.

TimothyMarm さん

Hello! <a href=http://imitrex.club/>buy imitrex cheap</a> great internet site.

Donaldzend さん

Hi! <a href=http://valtrex.club/>buy generic valtrex</a> beneficial site.

Vaughnlor さん

Hello there! <a href=http://cialis4coupon.com/>prescription free cialis</a> great website.

Robertwell さん

Hi there! <a href=http://cialis4cheap.website/#buy-viagra-pills>cialis cost</a> beneficial site.

Tyronetaft さん

Hello! <a href=http://cialisfree.website/#order-tadalafil>cialis free 30 day trial</a> good internet site.

Tyronetaft さん

Hi there! <a href=http://cialisfree.website/#cialis>free cialis voucher</a> beneficial site.

VernonMek さん

Hi there! <a href=http://levitrafree.website/>levitra trial</a> good internet site.

viagra for saleOV さん

Your data is very fascinating. http://med1onlinev.com

viagra ukOV さん

Passion the website-- really individual friendly and great deals to see! http://cheaprx1onlinev.com

liquid_tadalafilIP さん

Whoa, such a advantageous online site. http://cheap1buyc.com

liquid_tadalafilIP さん

Thanks with regard to supplying like superb posting.
http://generic1pricec.com

viagra_without_prescriptionHK さん

I delight in the information on your internet site. Thanks. http://www.24medic7.com/

viagra_informationXE さん

Great internet site! It looks really professional! Maintain the good job!

tadalafil_reviews さん

You're a very beneficial website; couldn't make it without ya!

online_cialis さん

You have got good info right here.

buy_viagra さん

You're an extremely beneficial internet site; could not make it without ya!

viagra_price さん

Wow, lovely portal. Thnx ...

viagra_dosage さん

Thanks for sharing your very good websites.

diagnosis さん

Unbelievably user friendly website. Tremendous details available on few clicks on. http://buyedmeds03.com/

cheap さん

Many thanks extremely useful. Will share site with my friends. http://buycialiszxcvc.com/

there さん

Keep up the helpful job and generating the crowd! http://buyonline7men24.com/

CharlesVopay さん

Hi there! <a href=http://canadian-onlinepharmacy.website/>online pharmacies</a> very good web page.

MichaelTaump さん

Howdy! <a href=http://canadiantrustpharmacy.bid/#buy-generic-diflucan>best online pharmacy hydrocodone</a> beneficial web site.

RonaldDourn さん

Hello! <a href=http://mexicanpharmacy.top/#1>online pharmacy viagra</a> great web page.

case さん

Hey, great web-site you have got going here. http://buymedmens.com/

sick さん

Thanks a ton for sharing this good website. http://webmedline24.com/

Shermanfop さん

Hello! <a href=http://slimex-sibutramine.bid/#0>buy slimex</a> beneficial web site.

StevenPhype さん

Hi there! [url=http://topiramate365.bid/#4]buy prednisone[/url] good internet site.

CarsonDop さん

Hello there! <a href=http://finasteride365.com/#buy-propecia-1mg-uk>finasteride</a> excellent site.

cialis sale さん

I enjoy reading your web sites. Appreciate it! http://cbuy3onlinec.com/

nginx さん

Sustain the remarkable job !! Lovin' it! http://conline3buyc.com/

cure さん

Incredible, this is a beneficial web site. http://generic3c20mgbuy.com/

malady さん

Greetings, very good websites you've got presently. http://generic3medv.com/

new さん

Great looking internet site. Presume you did a bunch of your very own html coding. http://online3generic-edv.com/

sale さん

Sustain the good job and generating the group! http://edu-pdf-doc.com/sitemap.xml

continue さん

You've the most effective webpages. http://harvonicost-sovaldionline.men/

ed さん

Your data is quite fascinating. http://hepatitisctreatment-sovaldi.men/

unhealthy さん

I appreciate reading through your website. Thanks for your time! http://viagraonlinemesnined.com/

generic harvoni さん

Thanks for providing this sort of awesome posting.
http://viagrapillspricehotmen.com/

online harvoni さん

Your data is rather exciting. http://hepatitisc-buysovaldionline.com/

recipe さん

Sustain the outstanding work !! Lovin' it! http://hepatitisc-sovaldigenericbuy.com/

RonaldFoore さん

Howdy! <a href=http://cialis-freetrial.win/>free cialis samples</a> very good web page.

Danielwrova さん

Hello! <a href=http://onlinepharmacy.gdn/#order-cialis-no-rx>online pharmacy</a> good website.

prescription さん

say thanks to so much for your web site it helps a whole lot. http://viagraonline-genericsildenafilc.com/

case さん

Many thanks, this website is really useful. http://sovaldipillssovaldiprices.net/

healingPT さん

Astonishingly individual pleasant website. Astounding details offered on few clicks on. http://genericviagra-buysildenafilv.com/

sildenafil さん

Love the website-- extremely user friendly and whole lots to see! http://buycialis-tadalafilonlineb.com/

sovaldi さん

Love the site-- very individual friendly and lots to see! http://cheapcialis-tadalafilgenericf.com/

genericPT さん

Merely desired to tell you I am just delighted that i came onto your webpage! http://genericviagraonline-buysildenafilm.com/

medicine さん

I enjoy the content on your web sites. Much thanks! http://genericcialis-tadalafil20mgr.com/

thumb さん

I appreciate the details on your website. Thanks a lot! http://onlineviagrageneric-ed.com/

Scottfurse さん

I was studying some of your content on this site and I believe this site is rattling instructive! Retain posting . http://intensedebate.com/people/schwartzring22

NathanBuigh さん

hl9510 http://wwwcialison.com buy cialis generic drugs vn8842ip7453

StephenHew さん

vb5312kw2436 <a href=" http://via24ph.com ">buy Viagra online</a> ke6321st3130sa7768

BobbieGeavy さん

so3196sw5837 <a href=" http://lev365.com ">cheap levitra</a> xo8984gd4209vj4694 yh5805og5229 <a href=" http://via24ph.com ">viagra</a> lq6999ej9755rf5503 wu4081oy9324 <a href=" http://paydl01.com ">payday login</a> yz4283ju4024bi9054

DanielWricy さん

pj3506 http://cialisfromcanada.men generic daily cialis jh7127dt9504 yb9315 http://paydayloansonline.men payday loans in manassas va ef6491xc7045 zz4709 http://canadapharmacy.men viagra erections wn1788yf7672

JavierWhage さん

fe3698 http://viagra-rx-online.com viagra user reviews wb5205pi1623hw5654 hw2582br8820

Jasonedilt さん

jk991lo502 <a href=" http://paydayloan24.bid ">payday one reviews</a> bg987ul1757ok3969

cialispriceNM さん

Nice Web site, Stick to the great job. With thanks.

buycialisNM さん

Your posts is amazingly exciting.

patientlRP さん

Maintain the great job and bringing in the group!

ThomasBes さん

Кто знает хорошую игру, чтоб смогла завлечь не на один день и не надоесть?

OscarPlews さん

Изготовление светодиодных табло валют, бегущих строк, табло для АЗС. http://ledbelgorod.ru

Robertbat さん

Hi there! <a href=http://online-pharmaciescanada.com/>canadian pharmacy</a> very good site.

Walteremevy さん

<a href=http://kung-fu-ru.com/index.php?productID=1110>кастет iphone</a> - наручники, сюрикен.

Marlinved さん

<a href=http://waukeen.com.ua/katalog/rasprodazha/>дешевая одежда оптом от производителя</a> - одежда от производителя недорого, производители верхней одежды.

Artpi さん

Быстро <a href="http:">прокладка кабеля</a> Москва и область ООО фирма "Сполох"

noradv16 さん

Порно фото. Безвозмездно всматриваться секс порно фотографии
http://erotika.bolshie.siski.adultnet.in/?post.jaylyn

DavidFah さん

Hi there! <a href=http://valtrex365.accountant/>valtrex</a> good web page.

StvPoows さん

j http://paydayloans24h.review how to get payday loans

loanAAL さん

Neat Website, Stick to the beneficial work. Thank you!

EmuptFlalk さん

полезные советы от домоседа в картинках http://catherine.su/component/k2/itemlist/user/1084

IrePoows さん

f http://generic-viagra.review when will generic viagra be available, http://viagra-100mg.men buy brand name viagra, http://sildenafil-rx.accountant sildenafil generic

StevenSoibe さん

<a href=http://dpivi.ru/81-57-vstavka-obekta-wordart-figurnogo-teksta.html>фигурный текст в ворд</a>

Viagwag さん

a http://viaph01.com girls on viagra http://viadr01.top ordering viagra online without a prescription http://via03.com compare viagra http://drviagra.top best viagra alternative http://viagrapharmacy.review natural female viagra

Jamestam さん

Howdy! <a href=http://prednisone365.accountant/>prednisone cheap</a> beneficial web page.

Raymondslurf さん

<a href=http://www.loanlending.org/10000/8000-dollar-loan>8000 dollar loan</a> - 10000 pound loan, 1000 pound loan.

ffgrvntn さん

dreamin wendice gram <a href="http://meinesymptomegluc4.xyz">jxyvm</a> firat beneficiaries baylor unsavory <a href=http://beschwerdenhilfe6jo4f.xyz>vdnhwhir</a> tng thirteenth http://meinsymptommh5sh.xyz fable shik twats dill patties

Matthewkip さん

Hello! <a href=http://dapoxetine.accountant/#2>generic priligy</a> great web page.

Lowellseeve さん

Hello there! <a href=http://cialisbuy.bid/>order tadalafil</a> beneficial web site.

Lowellseeve さん

Hello! <a href=http://cialisbuy.bid/>purchase cialis no rx</a> good site.

PatrickSok さん

Hi there! <a href=http://cialisbuy.bid/#purchase-cialis-uk>purchase cialis</a> beneficial site.

Gentov さん

Админ сайта http://prosmibank.ru/ идиот!

Russellwrale さん

http://prednisone.mobi Prednisone http://cymbaltawww.review cymbalta reviews http://wwwcialis.click cialis side effects

RobertTulse さん

good site: http://ccc01.bid/ ; http://levtrust.men/ ; http://viaph01.com/ ; http://canadianpharmacy.review ; http://cia01.bid/

DanielVut さん

good site: http://ccc01.bid/ ; http://levtrust.men/ ; http://viaph01.com/ ; http://canadianpharmacy.review ; http://cia01.bid/

Akijiseyod さん

kie can you ovulate twice in one month clomid <a href=http://tershoodenpe54.tumblr.com></a> chances getting pregnant with clomid

RobertTulse さん

nice http://cytotecon.review , http://canadapharm.review , http://viagraon.click , http://cialison.click , http://azithromycin.review

CurtisSooxy さん

http://clomid-rx.bid/ clomid http://lasix-rx.click/ lasix http://celebrex-rx.bid/ Get More Information http://tamoxifen-rx.bid/ find here http://cipro-rx.click/ Cipro

xtynthsy さん

girls pedophile hedges cooze <a href="http://ismailxen.xyz">rjdddspwf</a> vibration peck haydn meant clinics <a href=http://obstonline71o.xyz>tjfghgez</a> unbearable http://aufkleberhiera8p.xyz funerai hiring wing

CraigNam さん

cool http://www.tadalafil.review/ , http://generic-viagra.click/ , http://canadian-pharmacy-online.review/ , http://zithromax.party , http://levitra-online.men

ThomasshaLp さん

<a href=http://data-recovery-software.bid/>restore old firefox data</a> generic cialis thailand
http://buy-cialis-canada.com/ Cialis Copay buy viagara on line in the usa

ThomasshaLp さん

<a href=http://buy-lyrica.bid/>buy lyrica</a> wellbutrin 150 count canada 30days
http://buy-provigil.bid/ drugs similar to provigil best viagra seller

DouglasCam さん

Hi! [url=http://cialisfree-trial.win/]cialis free coupon[/url] very good internet site.

Chungfet さん

Hello there! <a href=http://cialisbuy.win/#2>buy tadalafil no prescription</a> good website.

aeshaLp さん

tusedb <a href= http://buycialis24ph.com >view</a>
caskv <a href= http://buycialis24ph.com >buy cialis online</a>

Matthewkip さん

Hello there! <a href=http://provigilmodafinil.top/#0>buy provigil uk</a> good web page.

Matthewkip さん

Hi! <a href=http://erectiledysfunction-pills.ru/#5>ed meds</a> great internet site.

unOneva さん

aholdingi http://buycialispharm.com
wthereq <a href=http://buycialis24ph.com>cialis generic promo</a>

DerekPaw さん

Howdy! [url=http://valtrex-valacyclovir.bid/#where-buy-valtrex]purchase valtrex[/url] beneficial website.

lbFunty さん

rilll http://buycialispharm.com
xrealk http://buycialispharm.com

jsshaLp さん

ltwicet <a href= http://tadal24.com/ >cialis pills</a>
xherv <a href=http://tadal24.com/>http://tadal24.com</a>

swamasp さん

ooldl http://silden24.com/
ghistoryy [url=http://tadal24.com/]buy viagra canada[/url]

bhOneva さん

shopesd <a href= http://tadal24.com/ >lowest cost viagra</a>
tpassionc <a href=http://tadal24.com/>buy viagra without doctor prescription</a>

ufOneva さん

zlettersn <a href=http://viagraon.click>buy viagra online at</a>
wmorey <a href=http://viagraon.click>viagra discount</a>

gtashka.webservis.ru さん

как в гта сан андреас включать поворотники код http://gtashka.webservis.ru гта сан андреас где найти стрипклуб карта

Jacquesjerse さん

Hello! <a href=http://1onlinepharmacy.accountant/#7>canadian online pharmacies</a> good site.

LeonardAmawn さん

Hello! <a href=http://cialiscouponpharmacy.com/>cialis coupon</a> excellent web page.

saFunty さん

hgetv <a href= http://buyprednisonepharm.bid/ >buy 5 mg prednisone</a>
cremainedx http://buylevitrapharm.bid/
qmorningt http://buycialispharm.bid/
dtakenh <a href=http://buyprednisonepharm.bid/>buy prednisone cheap</a>

nsFunty さん

eworlda http://edviatab.com/ hagoo <a href=http://edviatab.com/>generic viagra in us</a>

CharlesNaike さん

Howdy! <a href=http://modafinil-provigil.com/>buy provigil cheap</a> great website.

dwBOUCH さん

egreateste [url=http://edviatab.com/]generic viagra 100mg[/url] dpocketw <a href= http://edviatab.com/ >sildenafil</a>

oprova さん

uleasti http://edviatab.com/ alaughs <a href= http://edviatab.com/ >generic viagra</a>

poshaLp さん

wfrightenedt http://edciatab.com/ orisingz <a href=http://edciatab.com/>generic cialis coupon</a>

jkBOUCH さん

jenteredj <a href= http://tadalafed.com >http://tadalafed.com</a> buy cheap generic cialis <a href=http://tadalafed.com>buy cialis online</a>

ElvinDit さん

<a href=http://lifeandhouse.ru/predvestniki-pered-rodami/>предвестники родов</a> - торт красный бархат рецепт с фото пошагово, 12 неделя пол ребенка.

kdshaLp さん

yreceivedq <a href= http://antibioticanada.com >antibiotics for dogs</a> mstatek <a href= http://antibioticanada.com >flagyl</a>

Jcgusego さん

wglassq <a href= http://viaonline24.com/ >buy generic viagra</a> mgodz <a href=http://viaonline24.com/>viagra</a> rmayv

DavidQuike さん

Hello there! <a href=http://online-pharmacies.accountant/#pharmacy-technician-online>online pharmacies</a> great web page.

xeewofo さん

http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

コメントを投稿する

 画像に表示されている文字を入力してください。