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