1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/protocol/jingle_session.h"
6
7#include "base/bind.h"
8#include "base/rand_util.h"
9#include "base/single_thread_task_runner.h"
10#include "base/stl_util.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/thread_task_runner_handle.h"
13#include "base/time/time.h"
14#include "remoting/base/constants.h"
15#include "remoting/protocol/authenticator.h"
16#include "remoting/protocol/channel_authenticator.h"
17#include "remoting/protocol/channel_multiplexer.h"
18#include "remoting/protocol/content_description.h"
19#include "remoting/protocol/jingle_messages.h"
20#include "remoting/protocol/jingle_session_manager.h"
21#include "remoting/protocol/pseudotcp_channel_factory.h"
22#include "remoting/protocol/secure_channel_factory.h"
23#include "remoting/protocol/session_config.h"
24#include "remoting/protocol/stream_channel_factory.h"
25#include "remoting/signaling/iq_sender.h"
26#include "third_party/libjingle/source/talk/p2p/base/candidate.h"
27#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
28
29using buzz::XmlElement;
30
31namespace remoting {
32namespace protocol {
33
34namespace {
35// Delay after candidate creation before sending transport-info
36// message. This is neccessary to be able to pack multiple candidates
37// into one transport-info messages. The value needs to be greater
38// than zero because ports are opened asynchronously in the browser
39// process.
40const int kTransportInfoSendDelayMs = 2;
41
42// How long we should wait for a response from the other end. This value is used
43// for all requests except |transport-info|.
44const int kDefaultMessageTimeout = 10;
45
46// During a reconnection, it usually takes longer for the peer to respond due to
47// pending messages in the channel from the previous session.  From experiment,
48// it can take up to 20s for the session to reconnect. To make it safe, setting
49// the timeout to 30s.
50const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3;
51
52// Timeout for the transport-info messages.
53const int kTransportInfoTimeout = 10 * 60;
54
55// Name of the multiplexed channel.
56const char kMuxChannelName[] = "mux";
57
58ErrorCode AuthRejectionReasonToErrorCode(
59    Authenticator::RejectionReason reason) {
60  switch (reason) {
61    case Authenticator::INVALID_CREDENTIALS:
62      return AUTHENTICATION_FAILED;
63    case Authenticator::PROTOCOL_ERROR:
64      return INCOMPATIBLE_PROTOCOL;
65  }
66  NOTREACHED();
67  return UNKNOWN_ERROR;
68}
69
70}  // namespace
71
72JingleSession::JingleSession(JingleSessionManager* session_manager)
73    : session_manager_(session_manager),
74      event_handler_(NULL),
75      state_(INITIALIZING),
76      error_(OK),
77      config_is_set_(false),
78      weak_factory_(this) {
79}
80
81JingleSession::~JingleSession() {
82  channel_multiplexer_.reset();
83  STLDeleteContainerPointers(pending_requests_.begin(),
84                             pending_requests_.end());
85  STLDeleteContainerPointers(transport_info_requests_.begin(),
86                             transport_info_requests_.end());
87
88  DCHECK(channels_.empty());
89
90  session_manager_->SessionDestroyed(this);
91}
92
93void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
94  DCHECK(CalledOnValidThread());
95  DCHECK(event_handler);
96  event_handler_ = event_handler;
97}
98
99ErrorCode JingleSession::error() {
100  DCHECK(CalledOnValidThread());
101  return error_;
102}
103
104void JingleSession::StartConnection(
105    const std::string& peer_jid,
106    scoped_ptr<Authenticator> authenticator,
107    scoped_ptr<CandidateSessionConfig> config) {
108  DCHECK(CalledOnValidThread());
109  DCHECK(authenticator.get());
110  DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
111
112  peer_jid_ = peer_jid;
113  authenticator_ = authenticator.Pass();
114  candidate_config_ = config.Pass();
115
116  // Generate random session ID. There are usually not more than 1
117  // concurrent session per host, so a random 64-bit integer provides
118  // enough entropy. In the worst case connection will fail when two
119  // clients generate the same session ID concurrently.
120  session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
121
122  // Send session-initiate message.
123  JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
124                        session_id_);
125  message.initiator = session_manager_->signal_strategy_->GetLocalJid();
126  message.description.reset(
127      new ContentDescription(candidate_config_->Clone(),
128                             authenticator_->GetNextMessage()));
129  SendMessage(message);
130
131  SetState(CONNECTING);
132}
133
134void JingleSession::InitializeIncomingConnection(
135    const JingleMessage& initiate_message,
136    scoped_ptr<Authenticator> authenticator) {
137  DCHECK(CalledOnValidThread());
138  DCHECK(initiate_message.description.get());
139  DCHECK(authenticator.get());
140  DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
141
142  peer_jid_ = initiate_message.from;
143  authenticator_ = authenticator.Pass();
144  session_id_ = initiate_message.sid;
145  candidate_config_ = initiate_message.description->config()->Clone();
146
147  SetState(ACCEPTING);
148}
149
150void JingleSession::AcceptIncomingConnection(
151    const JingleMessage& initiate_message) {
152  DCHECK(config_is_set_);
153
154  // Process the first authentication message.
155  const buzz::XmlElement* first_auth_message =
156      initiate_message.description->authenticator_message();
157
158  if (!first_auth_message) {
159    CloseInternal(INCOMPATIBLE_PROTOCOL);
160    return;
161  }
162
163  DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
164  // |authenticator_| is owned, so Unretained() is safe here.
165  authenticator_->ProcessMessage(first_auth_message, base::Bind(
166      &JingleSession::ContinueAcceptIncomingConnection,
167      base::Unretained(this)));
168}
169
170void JingleSession::ContinueAcceptIncomingConnection() {
171  DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
172  if (authenticator_->state() == Authenticator::REJECTED) {
173    CloseInternal(AuthRejectionReasonToErrorCode(
174        authenticator_->rejection_reason()));
175    return;
176  }
177
178  // Send the session-accept message.
179  JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
180                        session_id_);
181
182  scoped_ptr<buzz::XmlElement> auth_message;
183  if (authenticator_->state() == Authenticator::MESSAGE_READY)
184    auth_message = authenticator_->GetNextMessage();
185
186  message.description.reset(
187      new ContentDescription(CandidateSessionConfig::CreateFrom(config_),
188                             auth_message.Pass()));
189  SendMessage(message);
190
191  // Update state.
192  SetState(CONNECTED);
193
194  if (authenticator_->state() == Authenticator::ACCEPTED) {
195    OnAuthenticated();
196  } else {
197    DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
198    if (authenticator_->started()) {
199      SetState(AUTHENTICATING);
200    }
201  }
202}
203
204const std::string& JingleSession::jid() {
205  DCHECK(CalledOnValidThread());
206  return peer_jid_;
207}
208
209const CandidateSessionConfig* JingleSession::candidate_config() {
210  DCHECK(CalledOnValidThread());
211  return candidate_config_.get();
212}
213
214const SessionConfig& JingleSession::config() {
215  DCHECK(CalledOnValidThread());
216  return config_;
217}
218
219void JingleSession::set_config(const SessionConfig& config) {
220  DCHECK(CalledOnValidThread());
221  DCHECK(!config_is_set_);
222  config_ = config;
223  config_is_set_ = true;
224}
225
226StreamChannelFactory* JingleSession::GetTransportChannelFactory() {
227  DCHECK(CalledOnValidThread());
228  return secure_channel_factory_.get();
229}
230
231StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
232  DCHECK(CalledOnValidThread());
233  if (!channel_multiplexer_.get()) {
234    channel_multiplexer_.reset(
235        new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName));
236  }
237  return channel_multiplexer_.get();
238}
239
240void JingleSession::Close() {
241  DCHECK(CalledOnValidThread());
242
243  CloseInternal(OK);
244}
245
246void JingleSession::AddPendingRemoteCandidates(Transport* channel,
247                                               const std::string& name) {
248  std::list<JingleMessage::NamedCandidate>::iterator it =
249      pending_remote_candidates_.begin();
250  while(it != pending_remote_candidates_.end()) {
251    if (it->name == name) {
252      channel->AddRemoteCandidate(it->candidate);
253      it = pending_remote_candidates_.erase(it);
254    } else {
255      ++it;
256    }
257  }
258}
259
260void JingleSession::CreateChannel(const std::string& name,
261                                  const ChannelCreatedCallback& callback) {
262  DCHECK(!channels_[name]);
263
264  scoped_ptr<Transport> channel =
265      session_manager_->transport_factory_->CreateTransport();
266  channel->Connect(name, this, callback);
267  AddPendingRemoteCandidates(channel.get(), name);
268  channels_[name] = channel.release();
269}
270
271void JingleSession::CancelChannelCreation(const std::string& name) {
272  ChannelsMap::iterator it = channels_.find(name);
273  if (it != channels_.end()) {
274    DCHECK(!it->second->is_connected());
275    delete it->second;
276    DCHECK(channels_.find(name) == channels_.end());
277  }
278}
279
280void JingleSession::OnTransportCandidate(Transport* transport,
281                                         const cricket::Candidate& candidate) {
282  pending_candidates_.push_back(JingleMessage::NamedCandidate(
283      transport->name(), candidate));
284
285  if (!transport_infos_timer_.IsRunning()) {
286    // Delay sending the new candidates in case we get more candidates
287    // that we can send in one message.
288    transport_infos_timer_.Start(
289        FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
290        this, &JingleSession::SendTransportInfo);
291  }
292}
293
294void JingleSession::OnTransportRouteChange(Transport* transport,
295                                           const TransportRoute& route) {
296  if (event_handler_)
297    event_handler_->OnSessionRouteChange(transport->name(), route);
298}
299
300void JingleSession::OnTransportFailed(Transport* transport) {
301  CloseInternal(CHANNEL_CONNECTION_ERROR);
302}
303
304void JingleSession::OnTransportDeleted(Transport* transport) {
305  ChannelsMap::iterator it = channels_.find(transport->name());
306  DCHECK_EQ(it->second, transport);
307  channels_.erase(it);
308}
309
310void JingleSession::SendMessage(const JingleMessage& message) {
311  scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
312      message.ToXml(),
313      base::Bind(&JingleSession::OnMessageResponse,
314                 base::Unretained(this),
315                 message.action));
316
317  int timeout = kDefaultMessageTimeout;
318  if (message.action == JingleMessage::SESSION_INITIATE ||
319      message.action == JingleMessage::SESSION_ACCEPT) {
320    timeout = kSessionInitiateAndAcceptTimeout;
321  }
322  if (request) {
323    request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
324    pending_requests_.insert(request.release());
325  } else {
326    LOG(ERROR) << "Failed to send a "
327               << JingleMessage::GetActionName(message.action) << " message";
328  }
329}
330
331void JingleSession::OnMessageResponse(
332    JingleMessage::ActionType request_type,
333    IqRequest* request,
334    const buzz::XmlElement* response) {
335  // Delete the request from the list of pending requests.
336  pending_requests_.erase(request);
337  delete request;
338
339  // Ignore all responses after session was closed.
340  if (state_ == CLOSED || state_ == FAILED)
341    return;
342
343  std::string type_str = JingleMessage::GetActionName(request_type);
344
345  // |response| will be NULL if the request timed out.
346  if (!response) {
347    LOG(ERROR) << type_str << " request timed out.";
348    CloseInternal(SIGNALING_TIMEOUT);
349    return;
350  } else {
351    const std::string& type =
352        response->Attr(buzz::QName(std::string(), "type"));
353    if (type != "result") {
354      LOG(ERROR) << "Received error in response to " << type_str
355                 << " message: \"" << response->Str()
356                 << "\". Terminating the session.";
357
358      // TODO(sergeyu): There may be different reasons for error
359      // here. Parse the response stanza to find failure reason.
360      CloseInternal(PEER_IS_OFFLINE);
361    }
362  }
363}
364
365void JingleSession::SendTransportInfo() {
366  JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
367  message.candidates.swap(pending_candidates_);
368
369  scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
370      message.ToXml(),
371      base::Bind(&JingleSession::OnTransportInfoResponse,
372                 base::Unretained(this)));
373  if (request) {
374    request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
375    transport_info_requests_.push_back(request.release());
376  } else {
377    LOG(ERROR) << "Failed to send a transport-info message";
378  }
379}
380
381void JingleSession::OnTransportInfoResponse(IqRequest* request,
382                                            const buzz::XmlElement* response) {
383  DCHECK(!transport_info_requests_.empty());
384
385  // Consider transport-info requests sent before this one lost and delete
386  // corresponding IqRequest objects.
387  while (transport_info_requests_.front() != request) {
388    delete transport_info_requests_.front();
389    transport_info_requests_.pop_front();
390  }
391
392  // Delete the |request| itself.
393  DCHECK_EQ(request, transport_info_requests_.front());
394  delete request;
395  transport_info_requests_.pop_front();
396
397  // Ignore transport-info timeouts.
398  if (!response) {
399    LOG(ERROR) << "transport-info request has timed out.";
400    return;
401  }
402
403  const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
404  if (type != "result") {
405    LOG(ERROR) << "Received error in response to transport-info message: \""
406               << response->Str() << "\". Terminating the session.";
407    CloseInternal(PEER_IS_OFFLINE);
408  }
409}
410
411void JingleSession::OnIncomingMessage(const JingleMessage& message,
412                                      const ReplyCallback& reply_callback) {
413  DCHECK(CalledOnValidThread());
414
415  if (message.from != peer_jid_) {
416    // Ignore messages received from a different Jid.
417    reply_callback.Run(JingleMessageReply::INVALID_SID);
418    return;
419  }
420
421  switch (message.action) {
422    case JingleMessage::SESSION_ACCEPT:
423      OnAccept(message, reply_callback);
424      break;
425
426    case JingleMessage::SESSION_INFO:
427      OnSessionInfo(message, reply_callback);
428      break;
429
430    case JingleMessage::TRANSPORT_INFO:
431      reply_callback.Run(JingleMessageReply::NONE);
432      ProcessTransportInfo(message);
433      break;
434
435    case JingleMessage::SESSION_TERMINATE:
436      OnTerminate(message, reply_callback);
437      break;
438
439    default:
440      reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
441  }
442}
443
444void JingleSession::OnAccept(const JingleMessage& message,
445                             const ReplyCallback& reply_callback) {
446  if (state_ != CONNECTING) {
447    reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
448    return;
449  }
450
451  reply_callback.Run(JingleMessageReply::NONE);
452
453  const buzz::XmlElement* auth_message =
454      message.description->authenticator_message();
455  if (!auth_message) {
456    DLOG(WARNING) << "Received session-accept without authentication message ";
457    CloseInternal(INCOMPATIBLE_PROTOCOL);
458    return;
459  }
460
461  if (!InitializeConfigFromDescription(message.description.get())) {
462    CloseInternal(INCOMPATIBLE_PROTOCOL);
463    return;
464  }
465
466  // In case there is transport information in the accept message.
467  ProcessTransportInfo(message);
468
469  SetState(CONNECTED);
470
471  DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
472  authenticator_->ProcessMessage(auth_message, base::Bind(
473      &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
474}
475
476void JingleSession::OnSessionInfo(const JingleMessage& message,
477                                  const ReplyCallback& reply_callback) {
478  if (!message.info.get() ||
479      !Authenticator::IsAuthenticatorMessage(message.info.get())) {
480    reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
481    return;
482  }
483
484  if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
485      authenticator_->state() != Authenticator::WAITING_MESSAGE) {
486    LOG(WARNING) << "Received unexpected authenticator message "
487                 << message.info->Str();
488    reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
489    CloseInternal(INCOMPATIBLE_PROTOCOL);
490    return;
491  }
492
493  reply_callback.Run(JingleMessageReply::NONE);
494
495  authenticator_->ProcessMessage(message.info.get(), base::Bind(
496      &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
497}
498
499void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
500  for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
501           message.candidates.begin();
502       it != message.candidates.end(); ++it) {
503    ChannelsMap::iterator channel = channels_.find(it->name);
504    if (channel != channels_.end()) {
505      channel->second->AddRemoteCandidate(it->candidate);
506    } else {
507      // Transport info was received before the channel was created.
508      // This could happen due to messages being reordered on the wire.
509      pending_remote_candidates_.push_back(*it);
510    }
511  }
512}
513
514void JingleSession::OnTerminate(const JingleMessage& message,
515                                const ReplyCallback& reply_callback) {
516  if (!is_session_active()) {
517    LOG(WARNING) << "Received unexpected session-terminate message.";
518    reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
519    return;
520  }
521
522  reply_callback.Run(JingleMessageReply::NONE);
523
524  switch (message.reason) {
525    case JingleMessage::SUCCESS:
526      if (state_ == CONNECTING) {
527        error_ = SESSION_REJECTED;
528      } else {
529        error_ = OK;
530      }
531      break;
532    case JingleMessage::DECLINE:
533      error_ = AUTHENTICATION_FAILED;
534      break;
535    case JingleMessage::CANCEL:
536      error_ = HOST_OVERLOAD;
537      break;
538    case JingleMessage::GENERAL_ERROR:
539      error_ = CHANNEL_CONNECTION_ERROR;
540      break;
541    case JingleMessage::INCOMPATIBLE_PARAMETERS:
542      error_ = INCOMPATIBLE_PROTOCOL;
543      break;
544    default:
545      error_ = UNKNOWN_ERROR;
546  }
547
548  if (error_ != OK) {
549    SetState(FAILED);
550  } else {
551    SetState(CLOSED);
552  }
553}
554
555bool JingleSession::InitializeConfigFromDescription(
556    const ContentDescription* description) {
557  DCHECK(description);
558
559  if (!description->config()->GetFinalConfig(&config_)) {
560    LOG(ERROR) << "session-accept does not specify configuration";
561    return false;
562  }
563  if (!candidate_config()->IsSupported(config_)) {
564    LOG(ERROR) << "session-accept specifies an invalid configuration";
565    return false;
566  }
567
568  return true;
569}
570
571void JingleSession::ProcessAuthenticationStep() {
572  DCHECK(CalledOnValidThread());
573  DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
574
575  if (state_ != CONNECTED && state_ != AUTHENTICATING) {
576    DCHECK(state_ == FAILED || state_ == CLOSED);
577    // The remote host closed the connection while the authentication was being
578    // processed asynchronously, nothing to do.
579    return;
580  }
581
582  if (authenticator_->state() == Authenticator::MESSAGE_READY) {
583    JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
584    message.info = authenticator_->GetNextMessage();
585    DCHECK(message.info.get());
586    SendMessage(message);
587  }
588  DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
589
590  // The current JingleSession object can be destroyed by event_handler of
591  // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
592  // pointer to crash.  To protect against it, we run ContinueAuthenticationStep
593  // asychronously using a weak pointer.
594  base::ThreadTaskRunnerHandle::Get()->PostTask(
595    FROM_HERE,
596    base::Bind(&JingleSession::ContinueAuthenticationStep,
597               weak_factory_.GetWeakPtr()));
598
599  if (authenticator_->started()) {
600    SetState(AUTHENTICATING);
601  }
602}
603
604void JingleSession::ContinueAuthenticationStep() {
605  if (authenticator_->state() == Authenticator::ACCEPTED) {
606    OnAuthenticated();
607  } else if (authenticator_->state() == Authenticator::REJECTED) {
608    CloseInternal(AuthRejectionReasonToErrorCode(
609        authenticator_->rejection_reason()));
610  }
611}
612
613void JingleSession::OnAuthenticated() {
614  pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
615  secure_channel_factory_.reset(
616      new SecureChannelFactory(pseudotcp_channel_factory_.get(),
617                               authenticator_.get()));
618
619  SetState(AUTHENTICATED);
620}
621
622void JingleSession::CloseInternal(ErrorCode error) {
623  DCHECK(CalledOnValidThread());
624
625  if (is_session_active()) {
626    // Send session-terminate message with the appropriate error code.
627    JingleMessage::Reason reason;
628    switch (error) {
629      case OK:
630        reason = JingleMessage::SUCCESS;
631        break;
632      case SESSION_REJECTED:
633      case AUTHENTICATION_FAILED:
634        reason = JingleMessage::DECLINE;
635        break;
636      case INCOMPATIBLE_PROTOCOL:
637        reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
638        break;
639      case HOST_OVERLOAD:
640        reason = JingleMessage::CANCEL;
641        break;
642      default:
643        reason = JingleMessage::GENERAL_ERROR;
644    }
645
646    JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
647                          session_id_);
648    message.reason = reason;
649    SendMessage(message);
650  }
651
652  error_ = error;
653
654  if (state_ != FAILED && state_ != CLOSED) {
655    if (error != OK) {
656      SetState(FAILED);
657    } else {
658      SetState(CLOSED);
659    }
660  }
661}
662
663void JingleSession::SetState(State new_state) {
664  DCHECK(CalledOnValidThread());
665
666  if (new_state != state_) {
667    DCHECK_NE(state_, CLOSED);
668    DCHECK_NE(state_, FAILED);
669
670    state_ = new_state;
671    if (event_handler_)
672      event_handler_->OnSessionStateChange(new_state);
673  }
674}
675
676bool JingleSession::is_session_active() {
677  return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
678        state_ == AUTHENTICATING || state_ == AUTHENTICATED;
679}
680
681}  // namespace protocol
682}  // namespace remoting
683