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 "talk/p2p/base/constants.h"
30#include "talk/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
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 rtc::MessageData {
56  int event, error;
57  EventData(int ev, int err = 0) : event(ev), error(err) { }
58};
59
60struct CreateTunnelData : public rtc::MessageData {
61  buzz::Jid jid;
62  std::string description;
63  rtc::Thread* thread;
64  rtc::StreamInterface* stream;
65};
66
67extern const rtc::ConstantLabel SESSION_STATES[];
68
69const 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
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, rtc::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
146rtc::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
158rtc::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(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
203TunnelSession* 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
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  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
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    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
336TunnelSession::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
346TunnelSession::~TunnelSession() {
347  ASSERT(client_ != NULL);
348  ASSERT(session_ == NULL);
349  ASSERT(channel_ == NULL);
350}
351
352rtc::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               << 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
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
433