Home | History | Annotate | Download | only in tunnel
      1 /*
      2  * libjingle
      3  * Copyright 2004--2008, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/base/basicdefs.h"
     29 #include "talk/base/basictypes.h"
     30 #include "talk/base/common.h"
     31 #include "talk/base/helpers.h"
     32 #include "talk/base/logging.h"
     33 #include "talk/base/stringutils.h"
     34 #include "talk/p2p/base/constants.h"
     35 #include "talk/p2p/base/transportchannel.h"
     36 #include "talk/xmllite/xmlelement.h"
     37 #include "pseudotcpchannel.h"
     38 #include "tunnelsessionclient.h"
     39 
     40 namespace cricket {
     41 
     42 const char NS_TUNNEL[] = "http://www.google.com/talk/tunnel";
     43 const buzz::StaticQName QN_TUNNEL_DESCRIPTION = { NS_TUNNEL, "description" };
     44 const buzz::StaticQName QN_TUNNEL_TYPE = { NS_TUNNEL, "type" };
     45 const char CN_TUNNEL[] = "tunnel";
     46 
     47 enum {
     48   MSG_CLOCK = 1,
     49   MSG_DESTROY,
     50   MSG_TERMINATE,
     51   MSG_EVENT,
     52   MSG_CREATE_TUNNEL,
     53 };
     54 
     55 struct EventData : public talk_base::MessageData {
     56   int event, error;
     57   EventData(int ev, int err = 0) : event(ev), error(err) { }
     58 };
     59 
     60 struct CreateTunnelData : public talk_base::MessageData {
     61   buzz::Jid jid;
     62   std::string description;
     63   talk_base::Thread* thread;
     64   talk_base::StreamInterface* stream;
     65 };
     66 
     67 extern const talk_base::ConstantLabel SESSION_STATES[];
     68 
     69 const talk_base::ConstantLabel SESSION_STATES[] = {
     70   KLABEL(Session::STATE_INIT),
     71   KLABEL(Session::STATE_SENTINITIATE),
     72   KLABEL(Session::STATE_RECEIVEDINITIATE),
     73   KLABEL(Session::STATE_SENTACCEPT),
     74   KLABEL(Session::STATE_RECEIVEDACCEPT),
     75   KLABEL(Session::STATE_SENTMODIFY),
     76   KLABEL(Session::STATE_RECEIVEDMODIFY),
     77   KLABEL(Session::STATE_SENTREJECT),
     78   KLABEL(Session::STATE_RECEIVEDREJECT),
     79   KLABEL(Session::STATE_SENTREDIRECT),
     80   KLABEL(Session::STATE_SENTTERMINATE),
     81   KLABEL(Session::STATE_RECEIVEDTERMINATE),
     82   KLABEL(Session::STATE_INPROGRESS),
     83   KLABEL(Session::STATE_DEINIT),
     84   LASTLABEL
     85 };
     86 
     87 ///////////////////////////////////////////////////////////////////////////////
     88 // TunnelContentDescription
     89 ///////////////////////////////////////////////////////////////////////////////
     90 
     91 struct TunnelContentDescription : public ContentDescription {
     92   std::string description;
     93 
     94   TunnelContentDescription(const std::string& desc) : description(desc) { }
     95   virtual ContentDescription* Copy() const {
     96     return new TunnelContentDescription(*this);
     97   }
     98 };
     99 
    100 ///////////////////////////////////////////////////////////////////////////////
    101 // TunnelSessionClientBase
    102 ///////////////////////////////////////////////////////////////////////////////
    103 
    104 TunnelSessionClientBase::TunnelSessionClientBase(const buzz::Jid& jid,
    105                                 SessionManager* manager, const std::string &ns)
    106   : jid_(jid), session_manager_(manager), namespace_(ns), shutdown_(false) {
    107   session_manager_->AddClient(namespace_, this);
    108 }
    109 
    110 TunnelSessionClientBase::~TunnelSessionClientBase() {
    111   shutdown_ = true;
    112   for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
    113        it != sessions_.end();
    114        ++it) {
    115      Session* session = (*it)->ReleaseSession(true);
    116      session_manager_->DestroySession(session);
    117   }
    118   session_manager_->RemoveClient(namespace_);
    119 }
    120 
    121 void TunnelSessionClientBase::OnSessionCreate(Session* session, bool received) {
    122   LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionCreate: received="
    123                << received;
    124   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    125   if (received)
    126     sessions_.push_back(
    127         MakeTunnelSession(session, talk_base::Thread::Current(), RESPONDER));
    128 }
    129 
    130 void TunnelSessionClientBase::OnSessionDestroy(Session* session) {
    131   LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionDestroy";
    132   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    133   if (shutdown_)
    134     return;
    135   for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
    136        it != sessions_.end();
    137        ++it) {
    138     if ((*it)->HasSession(session)) {
    139       VERIFY((*it)->ReleaseSession(false) == session);
    140       sessions_.erase(it);
    141       return;
    142     }
    143   }
    144 }
    145 
    146 talk_base::StreamInterface* TunnelSessionClientBase::CreateTunnel(
    147     const buzz::Jid& to, const std::string& description) {
    148   // Valid from any thread
    149   CreateTunnelData data;
    150   data.jid = to;
    151   data.description = description;
    152   data.thread = talk_base::Thread::Current();
    153   data.stream = NULL;
    154   session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
    155   return data.stream;
    156 }
    157 
    158 talk_base::StreamInterface* TunnelSessionClientBase::AcceptTunnel(
    159     Session* session) {
    160   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    161   TunnelSession* tunnel = NULL;
    162   for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
    163        it != sessions_.end();
    164        ++it) {
    165     if ((*it)->HasSession(session)) {
    166       tunnel = *it;
    167       break;
    168     }
    169   }
    170   ASSERT(tunnel != NULL);
    171 
    172   SessionDescription* answer = CreateAnswer(session->remote_description());
    173   if (answer == NULL)
    174     return NULL;
    175 
    176   session->Accept(answer);
    177   return tunnel->GetStream();
    178 }
    179 
    180 void TunnelSessionClientBase::DeclineTunnel(Session* session) {
    181   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    182   session->Reject(STR_TERMINATE_DECLINE);
    183 }
    184 
    185 void TunnelSessionClientBase::OnMessage(talk_base::Message* pmsg) {
    186   if (pmsg->message_id == MSG_CREATE_TUNNEL) {
    187     ASSERT(session_manager_->signaling_thread()->IsCurrent());
    188     CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
    189     SessionDescription* offer = CreateOffer(data->jid, data->description);
    190     if (offer == NULL) {
    191       return;
    192     }
    193 
    194     Session* session = session_manager_->CreateSession(jid_.Str(), namespace_);
    195     TunnelSession* tunnel = MakeTunnelSession(session, data->thread,
    196                                               INITIATOR);
    197     sessions_.push_back(tunnel);
    198     session->Initiate(data->jid.Str(), offer);
    199     data->stream = tunnel->GetStream();
    200   }
    201 }
    202 
    203 TunnelSession* TunnelSessionClientBase::MakeTunnelSession(
    204     Session* session, talk_base::Thread* stream_thread,
    205     TunnelSessionRole /*role*/) {
    206   return new TunnelSession(this, session, stream_thread);
    207 }
    208 
    209 ///////////////////////////////////////////////////////////////////////////////
    210 // TunnelSessionClient
    211 ///////////////////////////////////////////////////////////////////////////////
    212 
    213 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
    214                                          SessionManager* manager,
    215                                          const std::string &ns)
    216     : TunnelSessionClientBase(jid, manager, ns) {
    217 }
    218 
    219 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
    220                                          SessionManager* manager)
    221     : TunnelSessionClientBase(jid, manager, NS_TUNNEL) {
    222 }
    223 
    224 TunnelSessionClient::~TunnelSessionClient() {
    225 }
    226 
    227 
    228 bool TunnelSessionClient::ParseContent(SignalingProtocol protocol,
    229                                        const buzz::XmlElement* elem,
    230                                        ContentDescription** content,
    231                                        ParseError* error) {
    232   if (const buzz::XmlElement* type_elem = elem->FirstNamed(QN_TUNNEL_TYPE)) {
    233     *content = new TunnelContentDescription(type_elem->BodyText());
    234     return true;
    235   }
    236   return false;
    237 }
    238 
    239 bool TunnelSessionClient::WriteContent(
    240     SignalingProtocol protocol,
    241     const ContentDescription* untyped_content,
    242     buzz::XmlElement** elem, WriteError* error) {
    243   const TunnelContentDescription* content =
    244       static_cast<const TunnelContentDescription*>(untyped_content);
    245 
    246   buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true);
    247   buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE);
    248   type_elem->SetBodyText(content->description);
    249   root->AddElement(type_elem);
    250   *elem = root;
    251   return true;
    252 }
    253 
    254 SessionDescription* NewTunnelSessionDescription(
    255     const std::string& content_name, ContentDescription* content) {
    256   SessionDescription* sdesc = new SessionDescription();
    257   sdesc->AddContent(content_name, NS_TUNNEL, content);
    258   return sdesc;
    259 }
    260 
    261 bool FindTunnelContent(const cricket::SessionDescription* sdesc,
    262                        std::string* name,
    263                        const TunnelContentDescription** content) {
    264   const ContentInfo* cinfo = sdesc->FirstContentByType(NS_TUNNEL);
    265   if (cinfo == NULL)
    266     return false;
    267 
    268   *name = cinfo->name;
    269   *content = static_cast<const TunnelContentDescription*>(
    270       cinfo->description);
    271   return true;
    272 }
    273 
    274 void TunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
    275                                            Session *session) {
    276   std::string content_name;
    277   const TunnelContentDescription* content = NULL;
    278   if (!FindTunnelContent(session->remote_description(),
    279                          &content_name, &content)) {
    280     session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
    281     return;
    282   }
    283 
    284   SignalIncomingTunnel(this, jid, content->description, session);
    285 }
    286 
    287 SessionDescription* TunnelSessionClient::CreateOffer(
    288     const buzz::Jid &jid, const std::string &description) {
    289   SessionDescription* offer = NewTunnelSessionDescription(
    290       CN_TUNNEL, new TunnelContentDescription(description));
    291   talk_base::scoped_ptr<TransportDescription> tdesc(
    292       session_manager_->transport_desc_factory()->CreateOffer(
    293           TransportOptions(), NULL));
    294   if (tdesc.get()) {
    295     offer->AddTransportInfo(TransportInfo(CN_TUNNEL, *tdesc));
    296   } else {
    297     delete offer;
    298     offer = NULL;
    299   }
    300   return offer;
    301 }
    302 
    303 SessionDescription* TunnelSessionClient::CreateAnswer(
    304     const SessionDescription* offer) {
    305   std::string content_name;
    306   const TunnelContentDescription* offer_tunnel = NULL;
    307   if (!FindTunnelContent(offer, &content_name, &offer_tunnel))
    308     return NULL;
    309 
    310   SessionDescription* answer = NewTunnelSessionDescription(
    311       content_name, new TunnelContentDescription(offer_tunnel->description));
    312   const TransportInfo* tinfo = offer->GetTransportInfoByName(content_name);
    313   if (tinfo) {
    314     const TransportDescription* offer_tdesc = &tinfo->description;
    315     ASSERT(offer_tdesc != NULL);
    316     talk_base::scoped_ptr<TransportDescription> tdesc(
    317       session_manager_->transport_desc_factory()->CreateAnswer(
    318           offer_tdesc, TransportOptions(),  NULL));
    319     if (tdesc.get()) {
    320       answer->AddTransportInfo(TransportInfo(content_name, *tdesc));
    321     } else {
    322       delete answer;
    323       answer = NULL;
    324     }
    325   }
    326   return answer;
    327 }
    328 ///////////////////////////////////////////////////////////////////////////////
    329 // TunnelSession
    330 ///////////////////////////////////////////////////////////////////////////////
    331 
    332 //
    333 // Signalling thread methods
    334 //
    335 
    336 TunnelSession::TunnelSession(TunnelSessionClientBase* client, Session* session,
    337                              talk_base::Thread* stream_thread)
    338     : client_(client), session_(session), channel_(NULL) {
    339   ASSERT(client_ != NULL);
    340   ASSERT(session_ != NULL);
    341   session_->SignalState.connect(this, &TunnelSession::OnSessionState);
    342   channel_ = new PseudoTcpChannel(stream_thread, session_);
    343   channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed);
    344 }
    345 
    346 TunnelSession::~TunnelSession() {
    347   ASSERT(client_ != NULL);
    348   ASSERT(session_ == NULL);
    349   ASSERT(channel_ == NULL);
    350 }
    351 
    352 talk_base::StreamInterface* TunnelSession::GetStream() {
    353   ASSERT(channel_ != NULL);
    354   return channel_->GetStream();
    355 }
    356 
    357 bool TunnelSession::HasSession(Session* session) {
    358   ASSERT(NULL != session_);
    359   return (session_ == session);
    360 }
    361 
    362 Session* TunnelSession::ReleaseSession(bool channel_exists) {
    363   ASSERT(NULL != session_);
    364   ASSERT(NULL != channel_);
    365   Session* session = session_;
    366   session_->SignalState.disconnect(this);
    367   session_ = NULL;
    368   if (channel_exists)
    369     channel_->SignalChannelClosed.disconnect(this);
    370   channel_ = NULL;
    371   delete this;
    372   return session;
    373 }
    374 
    375 void TunnelSession::OnSessionState(BaseSession* session,
    376                                    BaseSession::State state) {
    377   LOG(LS_INFO) << "TunnelSession::OnSessionState("
    378                << talk_base::nonnull(
    379                     talk_base::FindLabel(state, SESSION_STATES), "Unknown")
    380                << ")";
    381   ASSERT(session == session_);
    382 
    383   switch (state) {
    384   case Session::STATE_RECEIVEDINITIATE:
    385     OnInitiate();
    386     break;
    387   case Session::STATE_SENTACCEPT:
    388   case Session::STATE_RECEIVEDACCEPT:
    389     OnAccept();
    390     break;
    391   case Session::STATE_SENTTERMINATE:
    392   case Session::STATE_RECEIVEDTERMINATE:
    393     OnTerminate();
    394     break;
    395   case Session::STATE_DEINIT:
    396     // ReleaseSession should have been called before this.
    397     ASSERT(false);
    398     break;
    399   default:
    400     break;
    401   }
    402 }
    403 
    404 void TunnelSession::OnInitiate() {
    405   ASSERT(client_ != NULL);
    406   ASSERT(session_ != NULL);
    407   client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_);
    408 }
    409 
    410 void TunnelSession::OnAccept() {
    411   ASSERT(channel_ != NULL);
    412   const ContentInfo* content =
    413       session_->remote_description()->FirstContentByType(NS_TUNNEL);
    414   ASSERT(content != NULL);
    415   VERIFY(channel_->Connect(
    416       content->name, "tcp", ICE_CANDIDATE_COMPONENT_DEFAULT));
    417 }
    418 
    419 void TunnelSession::OnTerminate() {
    420   ASSERT(channel_ != NULL);
    421   channel_->OnSessionTerminate(session_);
    422 }
    423 
    424 void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
    425   ASSERT(channel_ == channel);
    426   ASSERT(session_ != NULL);
    427   session_->Terminate();
    428 }
    429 
    430 ///////////////////////////////////////////////////////////////////////////////
    431 
    432 } // namespace cricket
    433