| OLD | NEW |
| (Empty) |
| 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 "pseudotcpchannel.h" | |
| 29 #include "webrtc/p2p/base/constants.h" | |
| 30 #include "webrtc/p2p/base/transportchannel.h" | |
| 31 #include "webrtc/libjingle/xmllite/xmlelement.h" | |
| 32 #include "tunnelsessionclient.h" | |
| 33 #include "webrtc/base/basicdefs.h" | |
| 34 #include "webrtc/base/basictypes.h" | |
| 35 #include "webrtc/base/common.h" | |
| 36 #include "webrtc/base/helpers.h" | |
| 37 #include "webrtc/base/logging.h" | |
| 38 #include "webrtc/base/stringutils.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 rtc::MessageData { | |
| 56 int event, error; | |
| 57 EventData(int ev, int err = 0) : event(ev), error(err) { } | |
| 58 }; | |
| 59 | |
| 60 struct CreateTunnelData : public rtc::MessageData { | |
| 61 buzz::Jid jid; | |
| 62 std::string description; | |
| 63 rtc::Thread* thread; | |
| 64 rtc::StreamInterface* stream; | |
| 65 }; | |
| 66 | |
| 67 extern const rtc::ConstantLabel SESSION_STATES[]; | |
| 68 | |
| 69 const rtc::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, rtc::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 rtc::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 = rtc::Thread::Current(); | |
| 153 data.stream = NULL; | |
| 154 session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data); | |
| 155 return data.stream; | |
| 156 } | |
| 157 | |
| 158 rtc::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(rtc::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, rtc::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 rtc::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 rtc::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 rtc::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 rtc::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 << rtc::nonnull( | |
| 379 rtc::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 | |
| OLD | NEW |