aboutsummaryrefslogtreecommitdiff
path: root/talk/session/tunnel/tunnelsessionclient.cc
diff options
context:
space:
mode:
Diffstat (limited to 'talk/session/tunnel/tunnelsessionclient.cc')
-rw-r--r--talk/session/tunnel/tunnelsessionclient.cc432
1 files changed, 432 insertions, 0 deletions
diff --git a/talk/session/tunnel/tunnelsessionclient.cc b/talk/session/tunnel/tunnelsessionclient.cc
new file mode 100644
index 0000000000..7d2a7d1d21
--- /dev/null
+++ b/talk/session/tunnel/tunnelsessionclient.cc
@@ -0,0 +1,432 @@
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pseudotcpchannel.h"
+#include "webrtc/p2p/base/constants.h"
+#include "webrtc/p2p/base/transportchannel.h"
+#include "webrtc/libjingle/xmllite/xmlelement.h"
+#include "tunnelsessionclient.h"
+#include "webrtc/base/basicdefs.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/stringutils.h"
+
+namespace cricket {
+
+const char NS_TUNNEL[] = "http://www.google.com/talk/tunnel";
+const buzz::StaticQName QN_TUNNEL_DESCRIPTION = { NS_TUNNEL, "description" };
+const buzz::StaticQName QN_TUNNEL_TYPE = { NS_TUNNEL, "type" };
+const char CN_TUNNEL[] = "tunnel";
+
+enum {
+ MSG_CLOCK = 1,
+ MSG_DESTROY,
+ MSG_TERMINATE,
+ MSG_EVENT,
+ MSG_CREATE_TUNNEL,
+};
+
+struct EventData : public rtc::MessageData {
+ int event, error;
+ EventData(int ev, int err = 0) : event(ev), error(err) { }
+};
+
+struct CreateTunnelData : public rtc::MessageData {
+ buzz::Jid jid;
+ std::string description;
+ rtc::Thread* thread;
+ rtc::StreamInterface* stream;
+};
+
+extern const rtc::ConstantLabel SESSION_STATES[];
+
+const rtc::ConstantLabel SESSION_STATES[] = {
+ KLABEL(Session::STATE_INIT),
+ KLABEL(Session::STATE_SENTINITIATE),
+ KLABEL(Session::STATE_RECEIVEDINITIATE),
+ KLABEL(Session::STATE_SENTACCEPT),
+ KLABEL(Session::STATE_RECEIVEDACCEPT),
+ KLABEL(Session::STATE_SENTMODIFY),
+ KLABEL(Session::STATE_RECEIVEDMODIFY),
+ KLABEL(Session::STATE_SENTREJECT),
+ KLABEL(Session::STATE_RECEIVEDREJECT),
+ KLABEL(Session::STATE_SENTREDIRECT),
+ KLABEL(Session::STATE_SENTTERMINATE),
+ KLABEL(Session::STATE_RECEIVEDTERMINATE),
+ KLABEL(Session::STATE_INPROGRESS),
+ KLABEL(Session::STATE_DEINIT),
+ LASTLABEL
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelContentDescription
+///////////////////////////////////////////////////////////////////////////////
+
+struct TunnelContentDescription : public ContentDescription {
+ std::string description;
+
+ TunnelContentDescription(const std::string& desc) : description(desc) { }
+ virtual ContentDescription* Copy() const {
+ return new TunnelContentDescription(*this);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClientBase
+///////////////////////////////////////////////////////////////////////////////
+
+TunnelSessionClientBase::TunnelSessionClientBase(const buzz::Jid& jid,
+ SessionManager* manager, const std::string &ns)
+ : jid_(jid), session_manager_(manager), namespace_(ns), shutdown_(false) {
+ session_manager_->AddClient(namespace_, this);
+}
+
+TunnelSessionClientBase::~TunnelSessionClientBase() {
+ shutdown_ = true;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ Session* session = (*it)->ReleaseSession(true);
+ session_manager_->DestroySession(session);
+ }
+ session_manager_->RemoveClient(namespace_);
+}
+
+void TunnelSessionClientBase::OnSessionCreate(Session* session, bool received) {
+ LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionCreate: received="
+ << received;
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (received)
+ sessions_.push_back(
+ MakeTunnelSession(session, rtc::Thread::Current(), RESPONDER));
+}
+
+void TunnelSessionClientBase::OnSessionDestroy(Session* session) {
+ LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionDestroy";
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (shutdown_)
+ return;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ VERIFY((*it)->ReleaseSession(false) == session);
+ sessions_.erase(it);
+ return;
+ }
+ }
+}
+
+rtc::StreamInterface* TunnelSessionClientBase::CreateTunnel(
+ const buzz::Jid& to, const std::string& description) {
+ // Valid from any thread
+ CreateTunnelData data;
+ data.jid = to;
+ data.description = description;
+ data.thread = rtc::Thread::Current();
+ data.stream = NULL;
+ session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
+ return data.stream;
+}
+
+rtc::StreamInterface* TunnelSessionClientBase::AcceptTunnel(
+ Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ TunnelSession* tunnel = NULL;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ tunnel = *it;
+ break;
+ }
+ }
+ ASSERT(tunnel != NULL);
+
+ SessionDescription* answer = CreateAnswer(session->remote_description());
+ if (answer == NULL)
+ return NULL;
+
+ session->Accept(answer);
+ return tunnel->GetStream();
+}
+
+void TunnelSessionClientBase::DeclineTunnel(Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session->Reject(STR_TERMINATE_DECLINE);
+}
+
+void TunnelSessionClientBase::OnMessage(rtc::Message* pmsg) {
+ if (pmsg->message_id == MSG_CREATE_TUNNEL) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
+ SessionDescription* offer = CreateOffer(data->jid, data->description);
+ if (offer == NULL) {
+ return;
+ }
+
+ Session* session = session_manager_->CreateSession(jid_.Str(), namespace_);
+ TunnelSession* tunnel = MakeTunnelSession(session, data->thread,
+ INITIATOR);
+ sessions_.push_back(tunnel);
+ session->Initiate(data->jid.Str(), offer);
+ data->stream = tunnel->GetStream();
+ }
+}
+
+TunnelSession* TunnelSessionClientBase::MakeTunnelSession(
+ Session* session, rtc::Thread* stream_thread,
+ TunnelSessionRole /*role*/) {
+ return new TunnelSession(this, session, stream_thread);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClient
+///////////////////////////////////////////////////////////////////////////////
+
+TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
+ SessionManager* manager,
+ const std::string &ns)
+ : TunnelSessionClientBase(jid, manager, ns) {
+}
+
+TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
+ SessionManager* manager)
+ : TunnelSessionClientBase(jid, manager, NS_TUNNEL) {
+}
+
+TunnelSessionClient::~TunnelSessionClient() {
+}
+
+
+bool TunnelSessionClient::ParseContent(SignalingProtocol protocol,
+ const buzz::XmlElement* elem,
+ ContentDescription** content,
+ ParseError* error) {
+ if (const buzz::XmlElement* type_elem = elem->FirstNamed(QN_TUNNEL_TYPE)) {
+ *content = new TunnelContentDescription(type_elem->BodyText());
+ return true;
+ }
+ return false;
+}
+
+bool TunnelSessionClient::WriteContent(
+ SignalingProtocol protocol,
+ const ContentDescription* untyped_content,
+ buzz::XmlElement** elem, WriteError* error) {
+ const TunnelContentDescription* content =
+ static_cast<const TunnelContentDescription*>(untyped_content);
+
+ buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true);
+ buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE);
+ type_elem->SetBodyText(content->description);
+ root->AddElement(type_elem);
+ *elem = root;
+ return true;
+}
+
+SessionDescription* NewTunnelSessionDescription(
+ const std::string& content_name, ContentDescription* content) {
+ SessionDescription* sdesc = new SessionDescription();
+ sdesc->AddContent(content_name, NS_TUNNEL, content);
+ return sdesc;
+}
+
+bool FindTunnelContent(const cricket::SessionDescription* sdesc,
+ std::string* name,
+ const TunnelContentDescription** content) {
+ const ContentInfo* cinfo = sdesc->FirstContentByType(NS_TUNNEL);
+ if (cinfo == NULL)
+ return false;
+
+ *name = cinfo->name;
+ *content = static_cast<const TunnelContentDescription*>(
+ cinfo->description);
+ return true;
+}
+
+void TunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
+ Session *session) {
+ std::string content_name;
+ const TunnelContentDescription* content = NULL;
+ if (!FindTunnelContent(session->remote_description(),
+ &content_name, &content)) {
+ session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
+ return;
+ }
+
+ SignalIncomingTunnel(this, jid, content->description, session);
+}
+
+SessionDescription* TunnelSessionClient::CreateOffer(
+ const buzz::Jid &jid, const std::string &description) {
+ SessionDescription* offer = NewTunnelSessionDescription(
+ CN_TUNNEL, new TunnelContentDescription(description));
+ rtc::scoped_ptr<TransportDescription> tdesc(
+ session_manager_->transport_desc_factory()->CreateOffer(
+ TransportOptions(), NULL));
+ if (tdesc.get()) {
+ offer->AddTransportInfo(TransportInfo(CN_TUNNEL, *tdesc));
+ } else {
+ delete offer;
+ offer = NULL;
+ }
+ return offer;
+}
+
+SessionDescription* TunnelSessionClient::CreateAnswer(
+ const SessionDescription* offer) {
+ std::string content_name;
+ const TunnelContentDescription* offer_tunnel = NULL;
+ if (!FindTunnelContent(offer, &content_name, &offer_tunnel))
+ return NULL;
+
+ SessionDescription* answer = NewTunnelSessionDescription(
+ content_name, new TunnelContentDescription(offer_tunnel->description));
+ const TransportInfo* tinfo = offer->GetTransportInfoByName(content_name);
+ if (tinfo) {
+ const TransportDescription* offer_tdesc = &tinfo->description;
+ ASSERT(offer_tdesc != NULL);
+ rtc::scoped_ptr<TransportDescription> tdesc(
+ session_manager_->transport_desc_factory()->CreateAnswer(
+ offer_tdesc, TransportOptions(), NULL));
+ if (tdesc.get()) {
+ answer->AddTransportInfo(TransportInfo(content_name, *tdesc));
+ } else {
+ delete answer;
+ answer = NULL;
+ }
+ }
+ return answer;
+}
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSession
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Signalling thread methods
+//
+
+TunnelSession::TunnelSession(TunnelSessionClientBase* client, Session* session,
+ rtc::Thread* stream_thread)
+ : client_(client), session_(session), channel_(NULL) {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ session_->SignalState.connect(this, &TunnelSession::OnSessionState);
+ channel_ = new PseudoTcpChannel(stream_thread, session_);
+ channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed);
+}
+
+TunnelSession::~TunnelSession() {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ == NULL);
+ ASSERT(channel_ == NULL);
+}
+
+rtc::StreamInterface* TunnelSession::GetStream() {
+ ASSERT(channel_ != NULL);
+ return channel_->GetStream();
+}
+
+bool TunnelSession::HasSession(Session* session) {
+ ASSERT(NULL != session_);
+ return (session_ == session);
+}
+
+Session* TunnelSession::ReleaseSession(bool channel_exists) {
+ ASSERT(NULL != session_);
+ ASSERT(NULL != channel_);
+ Session* session = session_;
+ session_->SignalState.disconnect(this);
+ session_ = NULL;
+ if (channel_exists)
+ channel_->SignalChannelClosed.disconnect(this);
+ channel_ = NULL;
+ delete this;
+ return session;
+}
+
+void TunnelSession::OnSessionState(BaseSession* session,
+ BaseSession::State state) {
+ LOG(LS_INFO) << "TunnelSession::OnSessionState("
+ << rtc::nonnull(
+ rtc::FindLabel(state, SESSION_STATES), "Unknown")
+ << ")";
+ ASSERT(session == session_);
+
+ switch (state) {
+ case Session::STATE_RECEIVEDINITIATE:
+ OnInitiate();
+ break;
+ case Session::STATE_SENTACCEPT:
+ case Session::STATE_RECEIVEDACCEPT:
+ OnAccept();
+ break;
+ case Session::STATE_SENTTERMINATE:
+ case Session::STATE_RECEIVEDTERMINATE:
+ OnTerminate();
+ break;
+ case Session::STATE_DEINIT:
+ // ReleaseSession should have been called before this.
+ ASSERT(false);
+ break;
+ default:
+ break;
+ }
+}
+
+void TunnelSession::OnInitiate() {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_);
+}
+
+void TunnelSession::OnAccept() {
+ ASSERT(channel_ != NULL);
+ const ContentInfo* content =
+ session_->remote_description()->FirstContentByType(NS_TUNNEL);
+ ASSERT(content != NULL);
+ VERIFY(channel_->Connect(
+ content->name, "tcp", ICE_CANDIDATE_COMPONENT_DEFAULT));
+}
+
+void TunnelSession::OnTerminate() {
+ ASSERT(channel_ != NULL);
+ channel_->OnSessionTerminate(session_);
+}
+
+void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
+ ASSERT(channel_ == channel);
+ ASSERT(session_ != NULL);
+ session_->Terminate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket