• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
EventDatacricket::EventData57   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 
TunnelContentDescriptioncricket::TunnelContentDescription94   TunnelContentDescription(const std::string& desc) : description(desc) { }
95 };
96 
97 ///////////////////////////////////////////////////////////////////////////////
98 // TunnelSessionClientBase
99 ///////////////////////////////////////////////////////////////////////////////
100 
TunnelSessionClientBase(const buzz::Jid & jid,SessionManager * manager,const std::string & ns)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 
~TunnelSessionClientBase()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 
OnSessionCreate(Session * session,bool received)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 
OnSessionDestroy(Session * session)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 
CreateTunnel(const buzz::Jid & to,const std::string & description)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 
AcceptTunnel(Session * session)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 
DeclineTunnel(Session * session)176 void TunnelSessionClientBase::DeclineTunnel(Session* session) {
177   ASSERT(session_manager_->signaling_thread()->IsCurrent());
178   session->Reject(STR_TERMINATE_DECLINE);
179 }
180 
OnMessage(talk_base::Message * pmsg)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 
MakeTunnelSession(Session * session,talk_base::Thread * stream_thread,TunnelSessionRole)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 
TunnelSessionClient(const buzz::Jid & jid,SessionManager * manager,const std::string & ns)205 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
206                                          SessionManager* manager,
207                                          const std::string &ns)
208     : TunnelSessionClientBase(jid, manager, ns) {
209 }
210 
TunnelSessionClient(const buzz::Jid & jid,SessionManager * manager)211 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
212                                          SessionManager* manager)
213     : TunnelSessionClientBase(jid, manager, NS_TUNNEL) {
214 }
215 
~TunnelSessionClient()216 TunnelSessionClient::~TunnelSessionClient() {
217 }
218 
219 
ParseContent(SignalingProtocol protocol,const buzz::XmlElement * elem,const ContentDescription ** content,ParseError * error)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 
WriteContent(SignalingProtocol protocol,const ContentDescription * untyped_content,buzz::XmlElement ** elem,WriteError * error)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 
NewTunnelSessionDescription(const std::string & content_name,const ContentDescription * content)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 
FindTunnelContent(const cricket::SessionDescription * sdesc,std::string * name,const TunnelContentDescription ** content)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 
OnIncomingTunnel(const buzz::Jid & jid,Session * session)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 
CreateOffer(const buzz::Jid & jid,const std::string & description)279 SessionDescription* TunnelSessionClient::CreateOffer(
280     const buzz::Jid &jid, const std::string &description) {
281   return NewTunnelSessionDescription(
282       CN_TUNNEL, new TunnelContentDescription(description));
283 }
284 
CreateAnswer(const SessionDescription * offer)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 
TunnelSession(TunnelSessionClientBase * client,Session * session,talk_base::Thread * stream_thread)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 
~TunnelSession()313 TunnelSession::~TunnelSession() {
314   ASSERT(client_ != NULL);
315   ASSERT(session_ == NULL);
316   ASSERT(channel_ == NULL);
317 }
318 
GetStream()319 talk_base::StreamInterface* TunnelSession::GetStream() {
320   ASSERT(channel_ != NULL);
321   return channel_->GetStream();
322 }
323 
HasSession(Session * session)324 bool TunnelSession::HasSession(Session* session) {
325   ASSERT(NULL != session_);
326   return (session_ == session);
327 }
328 
ReleaseSession(bool channel_exists)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 
OnSessionState(BaseSession * session,BaseSession::State state)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 
OnInitiate()371 void TunnelSession::OnInitiate() {
372   ASSERT(client_ != NULL);
373   ASSERT(session_ != NULL);
374   client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_);
375 }
376 
OnAccept()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 
OnTerminate()385 void TunnelSession::OnTerminate() {
386   ASSERT(channel_ != NULL);
387   channel_->OnSessionTerminate(session_);
388 }
389 
OnChannelClosed(PseudoTcpChannel * channel)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