1 // Copyright 2014 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.
5 #include "remoting/signaling/xmpp_signal_strategy.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/observer_list.h"
13 #include "base/rand_util.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/time/time.h"
19 #include "base/timer/timer.h"
20 #include "jingle/glue/proxy_resolving_client_socket.h"
21 #include "net/cert/cert_verifier.h"
22 #include "net/http/transport_security_state.h"
23 #include "net/socket/client_socket_factory.h"
24 #include "net/socket/client_socket_handle.h"
25 #include "net/socket/ssl_client_socket.h"
26 #include "net/url_request/url_request_context_getter.h"
27 #include "remoting/base/buffered_socket_writer.h"
28 #include "remoting/signaling/xmpp_login_handler.h"
29 #include "remoting/signaling/xmpp_stream_parser.h"
30 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
32 // Use 50 seconds keep-alive interval, in case routers terminate
33 // connections that are idle for more than a minute.
34 const int kKeepAliveIntervalSeconds
= 50;
36 const int kReadBufferSize
= 4096;
38 const int kDefaultXmppPort
= 5222;
39 const int kDefaultHttpsPort
= 443;
43 XmppSignalStrategy::XmppServerConfig::XmppServerConfig()
44 : port(kDefaultXmppPort
), use_tls(true) {
47 XmppSignalStrategy::XmppServerConfig::~XmppServerConfig() {
50 class XmppSignalStrategy::Core
: public XmppLoginHandler::Delegate
{
53 net::ClientSocketFactory
* socket_factory
,
54 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
55 const XmppServerConfig
& xmpp_server_config
);
60 State
GetState() const;
61 Error
GetError() const;
62 std::string
GetLocalJid() const;
63 void AddListener(Listener
* listener
);
64 void RemoveListener(Listener
* listener
);
65 bool SendStanza(scoped_ptr
<buzz::XmlElement
> stanza
);
67 void SetAuthInfo(const std::string
& username
,
68 const std::string
& auth_token
);
70 void VerifyNoListeners();
73 void OnSocketConnected(int result
);
74 void OnTlsConnected(int result
);
77 void OnReadResult(int result
);
78 void HandleReadResult(int result
);
80 // XmppLoginHandler::Delegate interface.
81 void SendMessage(const std::string
& message
) override
;
82 void StartTls() override
;
83 void OnHandshakeDone(const std::string
& jid
,
84 scoped_ptr
<XmppStreamParser
> parser
) override
;
85 void OnLoginHandlerError(SignalStrategy::Error error
) override
;
87 // Event handlers for XmppStreamParser.
88 void OnStanza(const scoped_ptr
<buzz::XmlElement
> stanza
);
91 void OnNetworkError(int error
);
95 net::ClientSocketFactory
* socket_factory_
;
96 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter_
;
97 XmppServerConfig xmpp_server_config_
;
99 // Used by the |socket_|.
100 scoped_ptr
<net::CertVerifier
> cert_verifier_
;
101 scoped_ptr
<net::TransportSecurityState
> transport_security_state_
;
103 scoped_ptr
<net::StreamSocket
> socket_
;
104 scoped_ptr
<BufferedSocketWriter
> writer_
;
105 scoped_refptr
<net::IOBuffer
> read_buffer_
;
109 scoped_ptr
<XmppLoginHandler
> login_handler_
;
110 scoped_ptr
<XmppStreamParser
> stream_parser_
;
115 ObserverList
<Listener
, true> listeners_
;
117 base::Timer keep_alive_timer_
;
119 base::ThreadChecker thread_checker_
;
121 DISALLOW_COPY_AND_ASSIGN(Core
);
124 XmppSignalStrategy::Core::Core(
125 net::ClientSocketFactory
* socket_factory
,
126 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
127 const XmppSignalStrategy::XmppServerConfig
& xmpp_server_config
)
128 : socket_factory_(socket_factory
),
129 request_context_getter_(request_context_getter
),
130 xmpp_server_config_(xmpp_server_config
),
131 read_pending_(false),
136 base::TimeDelta::FromSeconds(kKeepAliveIntervalSeconds
),
137 base::Bind(&Core::SendKeepAlive
, base::Unretained(this)),
140 // Non-secure connections are allowed only for debugging.
141 CHECK(xmpp_server_config_
.use_tls
);
145 XmppSignalStrategy::Core::~Core() {
149 void XmppSignalStrategy::Core::Connect() {
150 DCHECK(thread_checker_
.CalledOnValidThread());
152 // Disconnect first if we are currently connected.
157 FOR_EACH_OBSERVER(Listener
, listeners_
,
158 OnSignalStrategyStateChange(CONNECTING
));
160 socket_
.reset(new jingle_glue::ProxyResolvingClientSocket(
161 socket_factory_
, request_context_getter_
, net::SSLConfig(),
162 net::HostPortPair(xmpp_server_config_
.host
, xmpp_server_config_
.port
)));
164 int result
= socket_
->Connect(base::Bind(
165 &Core::OnSocketConnected
, base::Unretained(this)));
166 if (result
!= net::ERR_IO_PENDING
)
167 OnSocketConnected(result
);
170 void XmppSignalStrategy::Core::Disconnect() {
171 DCHECK(thread_checker_
.CalledOnValidThread());
174 login_handler_
.reset();
175 stream_parser_
.reset();
179 FOR_EACH_OBSERVER(Listener
, listeners_
,
180 OnSignalStrategyStateChange(DISCONNECTED
));
184 SignalStrategy::State
XmppSignalStrategy::Core::GetState() const {
185 DCHECK(thread_checker_
.CalledOnValidThread());
187 if (stream_parser_
) {
190 } else if (socket_
) {
197 SignalStrategy::Error
XmppSignalStrategy::Core::GetError() const {
198 DCHECK(thread_checker_
.CalledOnValidThread());
202 std::string
XmppSignalStrategy::Core::GetLocalJid() const {
203 DCHECK(thread_checker_
.CalledOnValidThread());
207 void XmppSignalStrategy::Core::AddListener(Listener
* listener
) {
208 DCHECK(thread_checker_
.CalledOnValidThread());
209 listeners_
.AddObserver(listener
);
212 void XmppSignalStrategy::Core::RemoveListener(Listener
* listener
) {
213 DCHECK(thread_checker_
.CalledOnValidThread());
214 listeners_
.RemoveObserver(listener
);
217 bool XmppSignalStrategy::Core::SendStanza(scoped_ptr
<buzz::XmlElement
> stanza
) {
218 DCHECK(thread_checker_
.CalledOnValidThread());
220 if (!stream_parser_
) {
221 VLOG(0) << "Dropping signalling message because XMPP "
222 "connection has been terminated.";
226 SendMessage(stanza
->Str());
230 void XmppSignalStrategy::Core::SetAuthInfo(const std::string
& username
,
231 const std::string
& auth_token
) {
232 DCHECK(thread_checker_
.CalledOnValidThread());
233 xmpp_server_config_
.username
= username
;
234 xmpp_server_config_
.auth_token
= auth_token
;
237 void XmppSignalStrategy::Core::SendMessage(const std::string
& message
) {
238 DCHECK(thread_checker_
.CalledOnValidThread());
239 scoped_refptr
<net::IOBufferWithSize
> buffer
=
240 new net::IOBufferWithSize(message
.size());
241 memcpy(buffer
->data(), message
.data(), message
.size());
242 writer_
->Write(buffer
, base::Closure());
245 void XmppSignalStrategy::Core::StartTls() {
246 DCHECK(thread_checker_
.CalledOnValidThread());
247 DCHECK(login_handler_
);
249 // Reset the writer so we don't try to write to the raw socket anymore.
250 DCHECK_EQ(writer_
->GetBufferSize(), 0);
253 DCHECK(!read_pending_
);
255 scoped_ptr
<net::ClientSocketHandle
> socket_handle(
256 new net::ClientSocketHandle());
257 socket_handle
->SetSocket(socket_
.Pass());
259 cert_verifier_
.reset(net::CertVerifier::CreateDefault());
260 transport_security_state_
.reset(new net::TransportSecurityState());
261 net::SSLClientSocketContext context
;
262 context
.cert_verifier
= cert_verifier_
.get();
263 context
.transport_security_state
= transport_security_state_
.get();
265 socket_
= socket_factory_
->CreateSSLClientSocket(
266 socket_handle
.Pass(),
267 net::HostPortPair(xmpp_server_config_
.host
, kDefaultHttpsPort
),
268 net::SSLConfig(), context
);
271 int result
= socket_
->Connect(
272 base::Bind(&Core::OnTlsConnected
, base::Unretained(this)));
273 if (result
!= net::ERR_IO_PENDING
)
274 OnTlsConnected(result
);
277 void XmppSignalStrategy::Core::OnHandshakeDone(
278 const std::string
& jid
,
279 scoped_ptr
<XmppStreamParser
> parser
) {
280 DCHECK(thread_checker_
.CalledOnValidThread());
283 stream_parser_
= parser
.Pass();
284 stream_parser_
->SetCallbacks(
285 base::Bind(&Core::OnStanza
, base::Unretained(this)),
286 base::Bind(&Core::OnParserError
, base::Unretained(this)));
288 // Don't need |login_handler_| anymore.
289 login_handler_
.reset();
291 FOR_EACH_OBSERVER(Listener
, listeners_
,
292 OnSignalStrategyStateChange(CONNECTED
));
295 void XmppSignalStrategy::Core::OnLoginHandlerError(
296 SignalStrategy::Error error
) {
297 DCHECK(thread_checker_
.CalledOnValidThread());
303 void XmppSignalStrategy::Core::OnStanza(
304 const scoped_ptr
<buzz::XmlElement
> stanza
) {
305 DCHECK(thread_checker_
.CalledOnValidThread());
307 ObserverListBase
<Listener
>::Iterator
it(&listeners_
);
308 for (Listener
* listener
= it
.GetNext(); listener
; listener
= it
.GetNext()) {
309 if (listener
->OnSignalStrategyIncomingStanza(stanza
.get()))
314 void XmppSignalStrategy::Core::OnParserError() {
315 DCHECK(thread_checker_
.CalledOnValidThread());
317 error_
= NETWORK_ERROR
;
321 void XmppSignalStrategy::Core::OnSocketConnected(int result
) {
322 DCHECK(thread_checker_
.CalledOnValidThread());
324 if (result
!= net::OK
) {
325 OnNetworkError(result
);
329 writer_
.reset(new BufferedSocketWriter());
330 writer_
->Init(socket_
.get(), base::Bind(&Core::OnNetworkError
,
331 base::Unretained(this)));
333 XmppLoginHandler::TlsMode tls_mode
;
334 if (xmpp_server_config_
.use_tls
) {
335 tls_mode
= (xmpp_server_config_
.port
== kDefaultXmppPort
)
336 ? XmppLoginHandler::TlsMode::WITH_HANDSHAKE
337 : XmppLoginHandler::TlsMode::WITHOUT_HANDSHAKE
;
339 tls_mode
= XmppLoginHandler::TlsMode::NO_TLS
;
342 // The server name is passed as to attribute in the <stream>. When connecting
343 // to talk.google.com it affects the certificate the server will use for TLS:
344 // talk.google.com uses gmail certificate when specified server is gmail.com
345 // or googlemail.com and google.com cert otherwise. In the same time it
346 // doesn't accept talk.google.com as target server. Here we use google.com
347 // server name when authenticating to talk.google.com. This ensures that the
348 // server will use google.com cert which will be accepted by the TLS
349 // implementation in Chrome (TLS API doesn't allow specifying domain other
350 // than the one that was passed to connect()).
351 std::string server
= xmpp_server_config_
.host
;
352 if (server
== "talk.google.com")
353 server
= "google.com";
355 login_handler_
.reset(
356 new XmppLoginHandler(server
, xmpp_server_config_
.username
,
357 xmpp_server_config_
.auth_token
, tls_mode
, this));
358 login_handler_
->Start();
363 void XmppSignalStrategy::Core::OnTlsConnected(int result
) {
364 DCHECK(thread_checker_
.CalledOnValidThread());
365 DCHECK(tls_pending_
);
366 tls_pending_
= false;
368 if (result
!= net::OK
) {
369 OnNetworkError(result
);
373 writer_
.reset(new BufferedSocketWriter());
374 writer_
->Init(socket_
.get(), base::Bind(&Core::OnNetworkError
,
375 base::Unretained(this)));
377 login_handler_
->OnTlsStarted();
382 void XmppSignalStrategy::Core::ReadSocket() {
383 DCHECK(thread_checker_
.CalledOnValidThread());
385 while (socket_
&& !read_pending_
&& !tls_pending_
) {
386 read_buffer_
= new net::IOBuffer(kReadBufferSize
);
387 int result
= socket_
->Read(
388 read_buffer_
.get(), kReadBufferSize
,
389 base::Bind(&Core::OnReadResult
, base::Unretained(this)));
390 HandleReadResult(result
);
394 void XmppSignalStrategy::Core::OnReadResult(int result
) {
395 DCHECK(thread_checker_
.CalledOnValidThread());
396 DCHECK(read_pending_
);
397 read_pending_
= false;
398 HandleReadResult(result
);
402 void XmppSignalStrategy::Core::HandleReadResult(int result
) {
403 DCHECK(thread_checker_
.CalledOnValidThread());
405 if (result
== net::ERR_IO_PENDING
) {
406 read_pending_
= true;
411 OnNetworkError(result
);
416 // Connection was closed by the server.
422 if (stream_parser_
) {
423 stream_parser_
->AppendData(std::string(read_buffer_
->data(), result
));
425 login_handler_
->OnDataReceived(std::string(read_buffer_
->data(), result
));
429 void XmppSignalStrategy::Core::OnNetworkError(int error
) {
430 DCHECK(thread_checker_
.CalledOnValidThread());
432 LOG(ERROR
) << "XMPP socket error " << error
;
433 error_
= NETWORK_ERROR
;
437 void XmppSignalStrategy::Core::SendKeepAlive() {
438 DCHECK(thread_checker_
.CalledOnValidThread());
440 if (GetState() == CONNECTED
)
444 XmppSignalStrategy::XmppSignalStrategy(
445 net::ClientSocketFactory
* socket_factory
,
446 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
447 const XmppServerConfig
& xmpp_server_config
)
448 : core_(new Core(socket_factory
,
449 request_context_getter
,
450 xmpp_server_config
)) {
453 XmppSignalStrategy::~XmppSignalStrategy() {
454 // All listeners should be removed at this point, so it's safe to detach
456 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE
, core_
.release());
459 void XmppSignalStrategy::Connect() {
463 void XmppSignalStrategy::Disconnect() {
467 SignalStrategy::State
XmppSignalStrategy::GetState() const {
468 return core_
->GetState();
471 SignalStrategy::Error
XmppSignalStrategy::GetError() const {
472 return core_
->GetError();
475 std::string
XmppSignalStrategy::GetLocalJid() const {
476 return core_
->GetLocalJid();
479 void XmppSignalStrategy::AddListener(Listener
* listener
) {
480 core_
->AddListener(listener
);
483 void XmppSignalStrategy::RemoveListener(Listener
* listener
) {
484 core_
->RemoveListener(listener
);
486 bool XmppSignalStrategy::SendStanza(scoped_ptr
<buzz::XmlElement
> stanza
) {
487 return core_
->SendStanza(stanza
.Pass());
490 std::string
XmppSignalStrategy::GetNextId() {
491 return base::Uint64ToString(base::RandUint64());
494 void XmppSignalStrategy::SetAuthInfo(const std::string
& username
,
495 const std::string
& auth_token
) {
496 core_
->SetAuthInfo(username
, auth_token
);
499 } // namespace remoting