blob: 71d0ce119859c822a13524b31359d3c4e4377d45 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
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 char NS_TUNNEL[] = "http://www.google.com/talk/tunnel";
43const buzz::StaticQName QN_TUNNEL_DESCRIPTION = { NS_TUNNEL, "description" };
44const buzz::StaticQName QN_TUNNEL_TYPE = { NS_TUNNEL, "type" };
45const char 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 virtual ContentDescription* Copy() const {
96 return new TunnelContentDescription(*this);
97 }
98};
99
100///////////////////////////////////////////////////////////////////////////////
101// TunnelSessionClientBase
102///////////////////////////////////////////////////////////////////////////////
103
104TunnelSessionClientBase::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
110TunnelSessionClientBase::~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
121void 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, talk_base::Thread::Current(), RESPONDER));
128}
129
130void 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
146talk_base::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 = talk_base::Thread::Current();
153 data.stream = NULL;
154 session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
155 return data.stream;
156}
157
158talk_base::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
180void TunnelSessionClientBase::DeclineTunnel(Session* session) {
181 ASSERT(session_manager_->signaling_thread()->IsCurrent());
182 session->Reject(STR_TERMINATE_DECLINE);
183}
184
185void TunnelSessionClientBase::OnMessage(talk_base::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
203TunnelSession* TunnelSessionClientBase::MakeTunnelSession(
204 Session* session, talk_base::Thread* stream_thread,
205 TunnelSessionRole /*role*/) {
206 return new TunnelSession(this, session, stream_thread);
207}
208
209///////////////////////////////////////////////////////////////////////////////
210// TunnelSessionClient
211///////////////////////////////////////////////////////////////////////////////
212
213TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
214 SessionManager* manager,
215 const std::string &ns)
216 : TunnelSessionClientBase(jid, manager, ns) {
217}
218
219TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
220 SessionManager* manager)
221 : TunnelSessionClientBase(jid, manager, NS_TUNNEL) {
222}
223
224TunnelSessionClient::~TunnelSessionClient() {
225}
226
227
228bool 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
239bool 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
254SessionDescription* 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
261bool 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
274void 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
287SessionDescription* TunnelSessionClient::CreateOffer(
288 const buzz::Jid &jid, const std::string &description) {
289 SessionDescription* offer = NewTunnelSessionDescription(
290 CN_TUNNEL, new TunnelContentDescription(description));
291 talk_base::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
303SessionDescription* 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 talk_base::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
336TunnelSession::TunnelSession(TunnelSessionClientBase* client, Session* session,
337 talk_base::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
346TunnelSession::~TunnelSession() {
347 ASSERT(client_ != NULL);
348 ASSERT(session_ == NULL);
349 ASSERT(channel_ == NULL);
350}
351
352talk_base::StreamInterface* TunnelSession::GetStream() {
353 ASSERT(channel_ != NULL);
354 return channel_->GetStream();
355}
356
357bool TunnelSession::HasSession(Session* session) {
358 ASSERT(NULL != session_);
359 return (session_ == session);
360}
361
362Session* 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
375void TunnelSession::OnSessionState(BaseSession* session,
376 BaseSession::State state) {
377 LOG(LS_INFO) << "TunnelSession::OnSessionState("
378 << talk_base::nonnull(
379 talk_base::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
404void TunnelSession::OnInitiate() {
405 ASSERT(client_ != NULL);
406 ASSERT(session_ != NULL);
407 client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_);
408}
409
410void 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
419void TunnelSession::OnTerminate() {
420 ASSERT(channel_ != NULL);
421 channel_->OnSessionTerminate(session_);
422}
423
424void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
425 ASSERT(channel_ == channel);
426 ASSERT(session_ != NULL);
427 session_->Terminate();
428}
429
430///////////////////////////////////////////////////////////////////////////////
431
432} // namespace cricket