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 std::string NS_TUNNEL("http://www.google.com/talk/tunnel");
     43 const buzz::QName QN_TUNNEL_DESCRIPTION(NS_TUNNEL, "description");
     44 const buzz::QName QN_TUNNEL_TYPE(NS_TUNNEL, "type");
     45 const std::string 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 };
     96 
     97 ///////////////////////////////////////////////////////////////////////////////
     98 // TunnelSessionClientBase
     99 ///////////////////////////////////////////////////////////////////////////////
    100 
    101 TunnelSessionClientBase::TunnelSessionClientBase(const buzz::Jid& jid,
    102                                 SessionManager* manager, const std::string &ns)
    103   : jid_(jid), session_manager_(manager), namespace_(ns), shutdown_(false) {
    104   session_manager_->AddClient(namespace_, this);
    105 }
    106 
    107 TunnelSessionClientBase::~TunnelSessionClientBase() {
    108   shutdown_ = true;
    109   for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
    110        it != sessions_.end();
    111        ++it) {
    112      Session* session = (*it)->ReleaseSession(true);
    113      session_manager_->DestroySession(session);
    114   }
    115   session_manager_->RemoveClient(namespace_);
    116 }
    117 
    118 void TunnelSessionClientBase::OnSessionCreate(Session* session, bool received) {
    119   LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionCreate: received="
    120                << received;
    121   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    122   if (received)
    123     sessions_.push_back(
    124         MakeTunnelSession(session, talk_base::Thread::Current(), RESPONDER));
    125 }
    126 
    127 void TunnelSessionClientBase::OnSessionDestroy(Session* session) {
    128   LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionDestroy";
    129   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    130   if (shutdown_)
    131     return;
    132   for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
    133        it != sessions_.end();
    134        ++it) {
    135     if ((*it)->HasSession(session)) {
    136       VERIFY((*it)->ReleaseSession(false) == session);
    137       sessions_.erase(it);
    138       return;
    139     }
    140   }
    141 }
    142 
    143 talk_base::StreamInterface* TunnelSessionClientBase::CreateTunnel(
    144     const buzz::Jid& to, const std::string& description) {
    145   // Valid from any thread
    146   CreateTunnelData data;
    147   data.jid = to;
    148   data.description = description;
    149   data.thread = talk_base::Thread::Current();
    150   session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
    151   return data.stream;
    152 }
    153 
    154 talk_base::StreamInterface* TunnelSessionClientBase::AcceptTunnel(
    155     Session* session) {
    156   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    157   TunnelSession* tunnel = NULL;
    158   for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
    159        it != sessions_.end();
    160        ++it) {
    161     if ((*it)->HasSession(session)) {
    162       tunnel = *it;
    163       break;
    164     }
    165   }
    166   ASSERT(tunnel != NULL);
    167 
    168   SessionDescription* answer = CreateAnswer(session->remote_description());
    169   if (answer == NULL)
    170     return NULL;
    171 
    172   session->Accept(answer);
    173   return tunnel->GetStream();
    174 }
    175 
    176 void TunnelSessionClientBase::DeclineTunnel(Session* session) {
    177   ASSERT(session_manager_->signaling_thread()->IsCurrent());
    178   session->Reject(STR_TERMINATE_DECLINE);
    179 }
    180 
    181 void TunnelSessionClientBase::OnMessage(talk_base::Message* pmsg) {
    182   if (pmsg->message_id == MSG_CREATE_TUNNEL) {
    183     ASSERT(session_manager_->signaling_thread()->IsCurrent());
    184     CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
    185     Session* session = session_manager_->CreateSession(jid_.Str(), namespace_);
    186     TunnelSession* tunnel = MakeTunnelSession(session, data->thread,
    187                                               INITIATOR);
    188     sessions_.push_back(tunnel);
    189     SessionDescription* offer = CreateOffer(data->jid, data->description);
    190     session->Initiate(data->jid.Str(), offer);
    191     data->stream = tunnel->GetStream();
    192   }
    193 }
    194 
    195 TunnelSession* TunnelSessionClientBase::MakeTunnelSession(
    196     Session* session, talk_base::Thread* stream_thread,
    197     TunnelSessionRole /*role*/) {
    198   return new TunnelSession(this, session, stream_thread);
    199 }
    200 
    201 ///////////////////////////////////////////////////////////////////////////////
    202 // TunnelSessionClient
    203 ///////////////////////////////////////////////////////////////////////////////
    204 
    205 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
    206                                          SessionManager* manager,
    207                                          const std::string &ns)
    208     : TunnelSessionClientBase(jid, manager, ns) {
    209 }
    210 
    211 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
    212                                          SessionManager* manager)
    213     : TunnelSessionClientBase(jid, manager, NS_TUNNEL) {
    214 }
    215 
    216 TunnelSessionClient::~TunnelSessionClient() {
    217 }
    218 
    219 
    220 bool TunnelSessionClient::ParseContent(SignalingProtocol protocol,
    221                                        const buzz::XmlElement* elem,
    222                                        const ContentDescription** content,
    223                                        ParseError* error) {
    224   if (const buzz::XmlElement* type_elem = elem->FirstNamed(QN_TUNNEL_TYPE)) {
    225     *content = new TunnelContentDescription(type_elem->BodyText());
    226     return true;
    227   }
    228   return false;
    229 }
    230 
    231 bool TunnelSessionClient::WriteContent(
    232     SignalingProtocol protocol,
    233     const ContentDescription* untyped_content,
    234     buzz::XmlElement** elem, WriteError* error) {
    235   const TunnelContentDescription* content =
    236       static_cast<const TunnelContentDescription*>(untyped_content);
    237 
    238   buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true);
    239   buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE);
    240   type_elem->SetBodyText(content->description);
    241   root->AddElement(type_elem);
    242   *elem = root;
    243   return true;
    244 }
    245 
    246 SessionDescription* NewTunnelSessionDescription(
    247     const std::string& content_name, const ContentDescription* content) {
    248   SessionDescription* sdesc = new SessionDescription();
    249   sdesc->AddContent(content_name, NS_TUNNEL, content);
    250   return sdesc;
    251 }
    252 
    253 bool FindTunnelContent(const cricket::SessionDescription* sdesc,
    254                        std::string* name,
    255                        const TunnelContentDescription** content) {
    256   const ContentInfo* cinfo = sdesc->FirstContentByType(NS_TUNNEL);
    257   if (cinfo == NULL)
    258     return false;
    259 
    260   *name = cinfo->name;
    261   *content = static_cast<const TunnelContentDescription*>(
    262       cinfo->description);
    263   return true;
    264 }
    265 
    266 void TunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
    267                                            Session *session) {
    268   std::string content_name;
    269   const TunnelContentDescription* content = NULL;
    270   if (!FindTunnelContent(session->remote_description(),
    271                          &content_name, &content)) {
    272     session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
    273     return;
    274   }
    275 
    276   SignalIncomingTunnel(this, jid, content->description, session);
    277 }
    278 
    279 SessionDescription* TunnelSessionClient::CreateOffer(
    280     const buzz::Jid &jid, const std::string &description) {
    281   return NewTunnelSessionDescription(
    282       CN_TUNNEL, new TunnelContentDescription(description));
    283 }
    284 
    285 SessionDescription* TunnelSessionClient::CreateAnswer(
    286     const SessionDescription* offer) {
    287   std::string content_name;
    288   const TunnelContentDescription* offer_tunnel = NULL;
    289   if (!FindTunnelContent(offer, &content_name, &offer_tunnel))
    290     return NULL;
    291 
    292   return NewTunnelSessionDescription(
    293       content_name, new TunnelContentDescription(offer_tunnel->description));
    294 }
    295 ///////////////////////////////////////////////////////////////////////////////
    296 // TunnelSession
    297 ///////////////////////////////////////////////////////////////////////////////
    298 
    299 //
    300 // Signalling thread methods
    301 //
    302 
    303 TunnelSession::TunnelSession(TunnelSessionClientBase* client, Session* session,
    304                              talk_base::Thread* stream_thread)
    305     : client_(client), session_(session), channel_(NULL) {
    306   ASSERT(client_ != NULL);
    307   ASSERT(session_ != NULL);
    308   session_->SignalState.connect(this, &TunnelSession::OnSessionState);
    309   channel_ = new PseudoTcpChannel(stream_thread, session_);
    310   channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed);
    311 }
    312 
    313 TunnelSession::~TunnelSession() {
    314   ASSERT(client_ != NULL);
    315   ASSERT(session_ == NULL);
    316   ASSERT(channel_ == NULL);
    317 }
    318 
    319 talk_base::StreamInterface* TunnelSession::GetStream() {
    320   ASSERT(channel_ != NULL);
    321   return channel_->GetStream();
    322 }
    323 
    324 bool TunnelSession::HasSession(Session* session) {
    325   ASSERT(NULL != session_);
    326   return (session_ == session);
    327 }
    328 
    329 Session* TunnelSession::ReleaseSession(bool channel_exists) {
    330   ASSERT(NULL != session_);
    331   ASSERT(NULL != channel_);
    332   Session* session = session_;
    333   session_->SignalState.disconnect(this);
    334   session_ = NULL;
    335   if (channel_exists)
    336     channel_->SignalChannelClosed.disconnect(this);
    337   channel_ = NULL;
    338   delete this;
    339   return session;
    340 }
    341 
    342 void TunnelSession::OnSessionState(BaseSession* session,
    343                                    BaseSession::State state) {
    344   LOG(LS_INFO) << "TunnelSession::OnSessionState("
    345                << talk_base::nonnull(
    346                     talk_base::FindLabel(state, SESSION_STATES), "Unknown")
    347                << ")";
    348   ASSERT(session == session_);
    349 
    350   switch (state) {
    351   case Session::STATE_RECEIVEDINITIATE:
    352     OnInitiate();
    353     break;
    354   case Session::STATE_SENTACCEPT:
    355   case Session::STATE_RECEIVEDACCEPT:
    356     OnAccept();
    357     break;
    358   case Session::STATE_SENTTERMINATE:
    359   case Session::STATE_RECEIVEDTERMINATE:
    360     OnTerminate();
    361     break;
    362   case Session::STATE_DEINIT:
    363     // ReleaseSession should have been called before this.
    364     ASSERT(false);
    365     break;
    366   default:
    367     break;
    368   }
    369 }
    370 
    371 void TunnelSession::OnInitiate() {
    372   ASSERT(client_ != NULL);
    373   ASSERT(session_ != NULL);
    374   client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_);
    375 }
    376 
    377 void TunnelSession::OnAccept() {
    378   ASSERT(channel_ != NULL);
    379   const ContentInfo* content =
    380       session_->remote_description()->FirstContentByType(NS_TUNNEL);
    381   ASSERT(content != NULL);
    382   VERIFY(channel_->Connect(content->name, "tcp"));
    383 }
    384 
    385 void TunnelSession::OnTerminate() {
    386   ASSERT(channel_ != NULL);
    387   channel_->OnSessionTerminate(session_);
    388 }
    389 
    390 void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
    391   ASSERT(channel_ == channel);
    392   ASSERT(session_ != NULL);
    393   session_->Terminate();
    394 }
    395 
    396 ///////////////////////////////////////////////////////////////////////////////
    397 
    398 } // namespace cricket
    399