blob: aa69d5c9c7a1df25691b37b1434eb8ea01908f48 [file] [log] [blame]
[email protected]6bad55c2012-01-24 20:50:271// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]22aae952011-09-12 23:47:512// 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/pepper_session.h"
6
7#include "base/bind.h"
8#include "base/rand_util.h"
9#include "base/stl_util.h"
10#include "base/string_number_conversions.h"
11#include "remoting/base/constants.h"
[email protected]b39e1822011-11-04 01:00:4912#include "remoting/jingle_glue/iq_sender.h"
[email protected]f44538512011-11-30 04:43:1713#include "remoting/protocol/authenticator.h"
[email protected]22aae952011-09-12 23:47:5114#include "remoting/protocol/content_description.h"
15#include "remoting/protocol/jingle_messages.h"
16#include "remoting/protocol/pepper_session_manager.h"
17#include "remoting/protocol/pepper_stream_channel.h"
18#include "third_party/libjingle/source/talk/p2p/base/candidate.h"
19#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
20
21using buzz::XmlElement;
22
23namespace remoting {
24namespace protocol {
25
26namespace {
27// Delay after candidate creation before sending transport-info
28// message. This is neccessary to be able to pack multiple candidates
29// into one transport-info messages. The value needs to be greater
30// than zero because ports are opened asynchronously in the browser
31// process.
32const int kTransportInfoSendDelayMs = 2;
33} // namespace
34
35PepperSession::PepperSession(PepperSessionManager* session_manager)
36 : session_manager_(session_manager),
37 state_(INITIALIZING),
[email protected]e3f03d02011-09-27 20:10:5138 error_(OK) {
[email protected]22aae952011-09-12 23:47:5139}
40
41PepperSession::~PepperSession() {
[email protected]22aae952011-09-12 23:47:5142 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
43 session_manager_->SessionDestroyed(this);
44}
45
[email protected]9e2a3132011-10-07 05:07:4046void PepperSession::SetStateChangeCallback(
47 const StateChangeCallback& callback) {
[email protected]22aae952011-09-12 23:47:5148 DCHECK(CalledOnValidThread());
[email protected]9e2a3132011-10-07 05:07:4049 state_change_callback_ = callback;
[email protected]22aae952011-09-12 23:47:5150}
51
[email protected]3cb66772012-01-25 00:30:5552void PepperSession::SetRouteChangeCallback(
53 const RouteChangeCallback& callback) {
54 // This callback is not used on the client side yet.
55 NOTREACHED();
56}
57
[email protected]e3f03d02011-09-27 20:10:5158Session::Error PepperSession::error() {
59 DCHECK(CalledOnValidThread());
60 return error_;
61}
62
[email protected]22aae952011-09-12 23:47:5163void PepperSession::StartConnection(
64 const std::string& peer_jid,
[email protected]5bf52312012-01-20 04:10:5265 scoped_ptr<Authenticator> authenticator,
66 scoped_ptr<CandidateSessionConfig> config,
[email protected]9e2a3132011-10-07 05:07:4067 const StateChangeCallback& state_change_callback) {
[email protected]22aae952011-09-12 23:47:5168 DCHECK(CalledOnValidThread());
[email protected]5bf52312012-01-20 04:10:5269 DCHECK(authenticator.get());
[email protected]f44538512011-11-30 04:43:1770 DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
[email protected]22aae952011-09-12 23:47:5171
72 peer_jid_ = peer_jid;
[email protected]5bf52312012-01-20 04:10:5273 authenticator_ = authenticator.Pass();
74 candidate_config_ = config.Pass();
[email protected]9e2a3132011-10-07 05:07:4075 state_change_callback_ = state_change_callback;
[email protected]22aae952011-09-12 23:47:5176
77 // Generate random session ID. There are usually not more than 1
78 // concurrent session per host, so a random 64-bit integer provides
79 // enough entropy. In the worst case connection will fail when two
80 // clients generate the same session ID concurrently.
81 session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
82
83 // Send session-initiate message.
84 JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
85 session_id_);
[email protected]08128a312012-01-03 21:02:3986 message.from = session_manager_->signal_strategy_->GetLocalJid();
[email protected]22aae952011-09-12 23:47:5187 message.description.reset(
[email protected]f44538512011-11-30 04:43:1788 new ContentDescription(candidate_config_->Clone(),
89 authenticator_->GetNextMessage()));
[email protected]b39e1822011-11-04 01:00:4990 initiate_request_.reset(session_manager_->iq_sender()->SendIq(
91 message.ToXml(),
92 base::Bind(&PepperSession::OnSessionInitiateResponse,
93 base::Unretained(this))));
[email protected]22aae952011-09-12 23:47:5194
95 SetState(CONNECTING);
96}
97
98void PepperSession::OnSessionInitiateResponse(
99 const buzz::XmlElement* response) {
100 const std::string& type = response->Attr(buzz::QName("", "type"));
101 if (type != "result") {
102 LOG(ERROR) << "Received error in response to session-initiate message: \""
103 << response->Str()
[email protected]349bea02011-11-05 00:52:27104 << "\". Terminating the session.";
[email protected]22aae952011-09-12 23:47:51105
106 // TODO(sergeyu): There may be different reasons for error
107 // here. Parse the response stanza to find failure reason.
[email protected]e3f03d02011-09-27 20:10:51108 OnError(PEER_IS_OFFLINE);
[email protected]22aae952011-09-12 23:47:51109 }
110}
111
112void PepperSession::OnError(Error error) {
113 error_ = error;
114 CloseInternal(true);
115}
116
117void PepperSession::CreateStreamChannel(
118 const std::string& name,
119 const StreamChannelCallback& callback) {
120 DCHECK(!channels_[name]);
121
[email protected]5bf52312012-01-20 04:10:52122 scoped_ptr<ChannelAuthenticator> channel_authenticator =
[email protected]f44538512011-11-30 04:43:17123 authenticator_->CreateChannelAuthenticator();
124 PepperStreamChannel* channel = new PepperStreamChannel(
125 this, name, callback);
[email protected]22aae952011-09-12 23:47:51126 channels_[name] = channel;
127 channel->Connect(session_manager_->pp_instance_,
[email protected]8d1f8752011-11-23 03:58:43128 session_manager_->transport_config_,
[email protected]5bf52312012-01-20 04:10:52129 channel_authenticator.Pass());
[email protected]22aae952011-09-12 23:47:51130}
131
132void PepperSession::CreateDatagramChannel(
133 const std::string& name,
134 const DatagramChannelCallback& callback) {
135 // TODO(sergeyu): Implement datagram channel support.
136 NOTREACHED();
137}
138
[email protected]f2d6a0032011-11-17 00:48:12139void PepperSession::CancelChannelCreation(const std::string& name) {
140 ChannelsMap::iterator it = channels_.find(name);
141 if (it != channels_.end() && !it->second->is_connected()) {
142 delete it->second;
143 DCHECK(!channels_[name]);
144 }
145}
146
[email protected]22aae952011-09-12 23:47:51147const std::string& PepperSession::jid() {
148 DCHECK(CalledOnValidThread());
149 return peer_jid_;
150}
151
152const CandidateSessionConfig* PepperSession::candidate_config() {
153 DCHECK(CalledOnValidThread());
154 return candidate_config_.get();
155}
156
157const SessionConfig& PepperSession::config() {
158 DCHECK(CalledOnValidThread());
159 return config_;
160}
161
162void PepperSession::set_config(const SessionConfig& config) {
163 DCHECK(CalledOnValidThread());
164 // set_config() should never be called on the client.
165 NOTREACHED();
166}
167
[email protected]22aae952011-09-12 23:47:51168void PepperSession::Close() {
169 DCHECK(CalledOnValidThread());
170
[email protected]0de37002011-12-06 07:30:16171 if (state_ == CONNECTING || state_ == CONNECTED || state_ == AUTHENTICATED) {
[email protected]22aae952011-09-12 23:47:51172 // Send session-terminate message.
173 JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
174 session_id_);
[email protected]b39e1822011-11-04 01:00:49175 scoped_ptr<IqRequest> terminate_request(
176 session_manager_->iq_sender()->SendIq(
177 message.ToXml(), IqSender::ReplyCallback()));
[email protected]22aae952011-09-12 23:47:51178 }
179
180 CloseInternal(false);
181}
182
183void PepperSession::OnIncomingMessage(const JingleMessage& message,
184 JingleMessageReply* reply) {
185 DCHECK(CalledOnValidThread());
186
187 if (message.from != peer_jid_) {
188 // Ignore messages received from a different Jid.
189 *reply = JingleMessageReply(JingleMessageReply::INVALID_SID);
190 return;
191 }
192
193 switch (message.action) {
194 case JingleMessage::SESSION_ACCEPT:
195 OnAccept(message, reply);
196 break;
197
[email protected]1bc9c7c2011-12-14 00:13:39198 case JingleMessage::SESSION_INFO:
199 OnSessionInfo(message, reply);
200 break;
201
[email protected]22aae952011-09-12 23:47:51202 case JingleMessage::TRANSPORT_INFO:
203 ProcessTransportInfo(message);
204 break;
205
[email protected]22aae952011-09-12 23:47:51206 case JingleMessage::SESSION_TERMINATE:
207 OnTerminate(message, reply);
208 break;
209
210 default:
211 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST);
212 }
213}
214
215void PepperSession::OnAccept(const JingleMessage& message,
216 JingleMessageReply* reply) {
217 if (state_ != CONNECTING) {
218 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST);
219 return;
220 }
221
[email protected]f44538512011-11-30 04:43:17222 const buzz::XmlElement* auth_message =
223 message.description->authenticator_message();
224 if (!auth_message) {
225 DLOG(WARNING) << "Received session-accept without authentication message "
226 << auth_message->Str();
227 OnError(INCOMPATIBLE_PROTOCOL);
228 return;
229 }
230
231 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
232 authenticator_->ProcessMessage(auth_message);
[email protected]f44538512011-11-30 04:43:17233
[email protected]22aae952011-09-12 23:47:51234 if (!InitializeConfigFromDescription(message.description.get())) {
[email protected]e3f03d02011-09-27 20:10:51235 OnError(INCOMPATIBLE_PROTOCOL);
[email protected]22aae952011-09-12 23:47:51236 return;
237 }
238
[email protected]1bc9c7c2011-12-14 00:13:39239 // In case there is transport information in the accept message.
240 ProcessTransportInfo(message);
241
[email protected]22aae952011-09-12 23:47:51242 SetState(CONNECTED);
243
[email protected]1bc9c7c2011-12-14 00:13:39244 // Process authentication.
245 if (authenticator_->state() == Authenticator::ACCEPTED) {
[email protected]0de37002011-12-06 07:30:16246 SetState(AUTHENTICATED);
[email protected]1bc9c7c2011-12-14 00:13:39247 } else {
248 ProcessAuthenticationStep();
249 }
250}
[email protected]0de37002011-12-06 07:30:16251
[email protected]1bc9c7c2011-12-14 00:13:39252void PepperSession::OnSessionInfo(const JingleMessage& message,
253 JingleMessageReply* reply) {
254 if (message.info.get() &&
255 Authenticator::IsAuthenticatorMessage(message.info.get())) {
256 if (state_ != CONNECTED ||
257 authenticator_->state() != Authenticator::WAITING_MESSAGE) {
258 LOG(WARNING) << "Received unexpected authenticator message "
259 << message.info->Str();
260 *reply = JingleMessageReply(JingleMessageReply::UNEXPECTED_REQUEST);
261 OnError(INCOMPATIBLE_PROTOCOL);
262 return;
263 }
264
265 authenticator_->ProcessMessage(message.info.get());
266 ProcessAuthenticationStep();
267 } else {
268 *reply = JingleMessageReply(JingleMessageReply::UNSUPPORTED_INFO);
269 }
[email protected]22aae952011-09-12 23:47:51270}
271
272void PepperSession::ProcessTransportInfo(const JingleMessage& message) {
273 for (std::list<cricket::Candidate>::const_iterator it =
274 message.candidates.begin();
275 it != message.candidates.end(); ++it) {
276 ChannelsMap::iterator channel = channels_.find(it->name());
277 if (channel == channels_.end()) {
278 LOG(WARNING) << "Received candidate for unknown channel " << it->name();
279 continue;
280 }
281 channel->second->AddRemoveCandidate(*it);
282 }
283}
284
[email protected]22aae952011-09-12 23:47:51285void PepperSession::OnTerminate(const JingleMessage& message,
286 JingleMessageReply* reply) {
287 if (state_ == CONNECTING) {
[email protected]e3f03d02011-09-27 20:10:51288 switch (message.reason) {
289 case JingleMessage::DECLINE:
290 OnError(SESSION_REJECTED);
291 break;
292
293 case JingleMessage::INCOMPATIBLE_PARAMETERS:
294 OnError(INCOMPATIBLE_PROTOCOL);
295 break;
296
297 default:
298 LOG(WARNING) << "Received session-terminate message "
299 "with an unexpected reason.";
300 OnError(SESSION_REJECTED);
301 }
[email protected]22aae952011-09-12 23:47:51302 return;
303 }
304
[email protected]1bc9c7c2011-12-14 00:13:39305 if (state_ != CONNECTED && state_ != AUTHENTICATED) {
306 LOG(WARNING) << "Received unexpected session-terminate message.";
[email protected]455a61682011-11-12 01:45:19307 }
308
[email protected]1bc9c7c2011-12-14 00:13:39309 if (message.reason == JingleMessage::SUCCESS) {
310 CloseInternal(false);
311 } else if (message.reason == JingleMessage::DECLINE) {
312 OnError(AUTHENTICATION_FAILED);
313 } else if (message.reason == JingleMessage::GENERAL_ERROR) {
314 OnError(CHANNEL_CONNECTION_ERROR);
315 } else if (message.reason == JingleMessage::INCOMPATIBLE_PARAMETERS) {
316 OnError(INCOMPATIBLE_PROTOCOL);
317 } else {
318 OnError(UNKNOWN_ERROR);
319 }
[email protected]22aae952011-09-12 23:47:51320}
321
322bool PepperSession::InitializeConfigFromDescription(
323 const ContentDescription* description) {
324 DCHECK(description);
325
[email protected]22aae952011-09-12 23:47:51326 if (!description->config()->GetFinalConfig(&config_)) {
327 LOG(ERROR) << "session-accept does not specify configuration";
328 return false;
329 }
330 if (!candidate_config()->IsSupported(config_)) {
331 LOG(ERROR) << "session-accept specifies an invalid configuration";
332 return false;
333 }
334
335 return true;
336}
337
[email protected]1bc9c7c2011-12-14 00:13:39338void PepperSession::ProcessAuthenticationStep() {
339 DCHECK_EQ(state_, CONNECTED);
340
341 if (authenticator_->state() == Authenticator::MESSAGE_READY) {
342 JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
[email protected]5bf52312012-01-20 04:10:52343 message.info = authenticator_->GetNextMessage();
[email protected]1bc9c7c2011-12-14 00:13:39344 DCHECK(message.info.get());
345
346 session_info_request_.reset(session_manager_->iq_sender()->SendIq(
347 message.ToXml(), base::Bind(
348 &PepperSession::OnSessionInfoResponse,
349 base::Unretained(this))));
350 }
351 DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
352
353 if (authenticator_->state() == Authenticator::ACCEPTED) {
354 SetState(AUTHENTICATED);
355 } else if (authenticator_->state() == Authenticator::REJECTED) {
[email protected]6bad55c2012-01-24 20:50:27356 switch (authenticator_->rejection_reason()) {
357 case Authenticator::INVALID_CREDENTIALS:
358 OnError(AUTHENTICATION_FAILED);
359 break;
360 case Authenticator::PROTOCOL_ERROR:
361 OnError(INCOMPATIBLE_PROTOCOL);
362 break;
363 }
[email protected]1bc9c7c2011-12-14 00:13:39364 }
365}
366
367void PepperSession::OnSessionInfoResponse(const buzz::XmlElement* response) {
368 const std::string& type = response->Attr(buzz::QName("", "type"));
369 if (type != "result") {
370 LOG(ERROR) << "Received error in response to session-info message: \""
371 << response->Str()
372 << "\". Terminating the session.";
[email protected]6bad55c2012-01-24 20:50:27373 OnError(INCOMPATIBLE_PROTOCOL);
[email protected]1bc9c7c2011-12-14 00:13:39374 }
375}
376
[email protected]22aae952011-09-12 23:47:51377void PepperSession::AddLocalCandidate(const cricket::Candidate& candidate) {
378 pending_candidates_.push_back(candidate);
379
380 if (!transport_infos_timer_.IsRunning()) {
381 // Delay sending the new candidates in case we get more candidates
382 // that we can send in one message.
383 transport_infos_timer_.Start(
384 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
385 this, &PepperSession::SendTransportInfo);
386 }
387}
388
[email protected]349bea02011-11-05 00:52:27389void PepperSession::OnTransportInfoResponse(const buzz::XmlElement* response) {
390 const std::string& type = response->Attr(buzz::QName("", "type"));
391 if (type != "result") {
392 LOG(ERROR) << "Received error in response to session-initiate message: \""
393 << response->Str()
394 << "\". Terminating the session.";
395
396 if (state_ == CONNECTING) {
397 OnError(PEER_IS_OFFLINE);
398 } else {
399 // Host has disconnected without sending session-terminate message.
400 CloseInternal(false);
401 }
402 }
403}
404
[email protected]22aae952011-09-12 23:47:51405void PepperSession::OnDeleteChannel(PepperChannel* channel) {
406 ChannelsMap::iterator it = channels_.find(channel->name());
407 DCHECK_EQ(it->second, channel);
408 channels_.erase(it);
409}
410
411void PepperSession::SendTransportInfo() {
412 JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
413 message.candidates.swap(pending_candidates_);
[email protected]349bea02011-11-05 00:52:27414 transport_info_request_.reset(session_manager_->iq_sender()->SendIq(
415 message.ToXml(), base::Bind(
416 &PepperSession::OnTransportInfoResponse,
417 base::Unretained(this))));
[email protected]22aae952011-09-12 23:47:51418}
419
[email protected]22aae952011-09-12 23:47:51420
421void PepperSession::CloseInternal(bool failed) {
422 DCHECK(CalledOnValidThread());
423
424 if (state_ != FAILED && state_ != CLOSED) {
[email protected]22aae952011-09-12 23:47:51425 if (failed)
426 SetState(FAILED);
427 else
428 SetState(CLOSED);
429 }
430}
431
432void PepperSession::SetState(State new_state) {
433 DCHECK(CalledOnValidThread());
434
435 if (new_state != state_) {
436 DCHECK_NE(state_, CLOSED);
437 DCHECK_NE(state_, FAILED);
438
439 state_ = new_state;
[email protected]9e2a3132011-10-07 05:07:40440 if (!state_change_callback_.is_null())
441 state_change_callback_.Run(new_state);
[email protected]22aae952011-09-12 23:47:51442 }
443}
444
445} // namespace protocol
446} // namespace remoting