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 40namespace cricket { 41 42const std::string NS_TUNNEL("http://www.google.com/talk/tunnel"); 43const buzz::QName QN_TUNNEL_DESCRIPTION(NS_TUNNEL, "description"); 44const buzz::QName QN_TUNNEL_TYPE(NS_TUNNEL, "type"); 45const std::string CN_TUNNEL("tunnel"); 46 47enum { 48 MSG_CLOCK = 1, 49 MSG_DESTROY, 50 MSG_TERMINATE, 51 MSG_EVENT, 52 MSG_CREATE_TUNNEL, 53}; 54 55struct EventData : public talk_base::MessageData { 56 int event, error; 57 EventData(int ev, int err = 0) : event(ev), error(err) { } 58}; 59 60struct 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 67extern const talk_base::ConstantLabel SESSION_STATES[]; 68 69const 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 91struct TunnelContentDescription : public ContentDescription { 92 std::string description; 93 94 TunnelContentDescription(const std::string& desc) : description(desc) { } 95}; 96 97/////////////////////////////////////////////////////////////////////////////// 98// TunnelSessionClientBase 99/////////////////////////////////////////////////////////////////////////////// 100 101TunnelSessionClientBase::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 107TunnelSessionClientBase::~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 118void 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 127void 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 143talk_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 154talk_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 176void TunnelSessionClientBase::DeclineTunnel(Session* session) { 177 ASSERT(session_manager_->signaling_thread()->IsCurrent()); 178 session->Reject(STR_TERMINATE_DECLINE); 179} 180 181void 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 195TunnelSession* 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 205TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid, 206 SessionManager* manager, 207 const std::string &ns) 208 : TunnelSessionClientBase(jid, manager, ns) { 209} 210 211TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid, 212 SessionManager* manager) 213 : TunnelSessionClientBase(jid, manager, NS_TUNNEL) { 214} 215 216TunnelSessionClient::~TunnelSessionClient() { 217} 218 219 220bool 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 231bool 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 246SessionDescription* 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 253bool 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 266void 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 279SessionDescription* TunnelSessionClient::CreateOffer( 280 const buzz::Jid &jid, const std::string &description) { 281 return NewTunnelSessionDescription( 282 CN_TUNNEL, new TunnelContentDescription(description)); 283} 284 285SessionDescription* 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 303TunnelSession::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 313TunnelSession::~TunnelSession() { 314 ASSERT(client_ != NULL); 315 ASSERT(session_ == NULL); 316 ASSERT(channel_ == NULL); 317} 318 319talk_base::StreamInterface* TunnelSession::GetStream() { 320 ASSERT(channel_ != NULL); 321 return channel_->GetStream(); 322} 323 324bool TunnelSession::HasSession(Session* session) { 325 ASSERT(NULL != session_); 326 return (session_ == session); 327} 328 329Session* 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 342void 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 371void TunnelSession::OnInitiate() { 372 ASSERT(client_ != NULL); 373 ASSERT(session_ != NULL); 374 client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_); 375} 376 377void 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 385void TunnelSession::OnTerminate() { 386 ASSERT(channel_ != NULL); 387 channel_->OnSessionTerminate(session_); 388} 389 390void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) { 391 ASSERT(channel_ == channel); 392 ASSERT(session_ != NULL); 393 session_->Terminate(); 394} 395 396/////////////////////////////////////////////////////////////////////////////// 397 398} // namespace cricket 399