1 // Copyright 2013 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 // A standalone tool for testing MCS connections and the MCS client on their
13 #include "base/at_exit.h"
14 #include "base/command_line.h"
15 #include "base/compiler_specific.h"
16 #include "base/logging.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/run_loop.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/threading/thread.h"
23 #include "base/threading/worker_pool.h"
24 #include "base/time/default_clock.h"
25 #include "base/values.h"
26 #include "google_apis/gcm/base/fake_encryptor.h"
27 #include "google_apis/gcm/base/mcs_message.h"
28 #include "google_apis/gcm/base/mcs_util.h"
29 #include "google_apis/gcm/engine/checkin_request.h"
30 #include "google_apis/gcm/engine/connection_factory_impl.h"
31 #include "google_apis/gcm/engine/gcm_store_impl.h"
32 #include "google_apis/gcm/engine/gservices_settings.h"
33 #include "google_apis/gcm/engine/mcs_client.h"
34 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
35 #include "net/base/host_mapping_rules.h"
36 #include "net/base/net_log_logger.h"
37 #include "net/cert/cert_verifier.h"
38 #include "net/dns/host_resolver.h"
39 #include "net/http/http_auth_handler_factory.h"
40 #include "net/http/http_network_session.h"
41 #include "net/http/http_server_properties_impl.h"
42 #include "net/http/transport_security_state.h"
43 #include "net/socket/client_socket_factory.h"
44 #include "net/socket/ssl_client_socket.h"
45 #include "net/ssl/default_server_bound_cert_store.h"
46 #include "net/ssl/server_bound_cert_service.h"
47 #include "net/url_request/url_request_test_util.h"
49 #if defined(OS_MACOSX)
50 #include "base/mac/scoped_nsautorelease_pool.h"
53 // This is a simple utility that initializes an mcs client and
54 // prints out any events.
58 const net::BackoffEntry::Policy kDefaultBackoffPolicy
= {
59 // Number of initial errors (in sequence) to ignore before applying
60 // exponential back-off rules.
63 // Initial delay for exponential back-off in ms.
66 // Factor by which the waiting time will be multiplied.
69 // Fuzzing percentage. ex: 10% will spread requests randomly
70 // between 90%-100% of the calculated time.
73 // Maximum amount of time we are willing to delay our request in ms.
74 1000 * 60 * 5, // 5 minutes.
76 // Time to keep an entry from being discarded even when it
77 // has no significant state, -1 to never discard.
80 // Don't use initial delay unless the last request was an error.
84 // Default values used to communicate with the check-in server.
85 const char kChromeVersion
[] = "Chrome MCS Probe";
87 // The default server to communicate with.
88 const char kMCSServerHost
[] = "mtalk.google.com";
89 const uint16 kMCSServerPort
= 5228;
91 // Command line switches.
92 const char kRMQFileName
[] = "rmq_file";
93 const char kAndroidIdSwitch
[] = "android_id";
94 const char kSecretSwitch
[] = "secret";
95 const char kLogFileSwitch
[] = "log-file";
96 const char kIgnoreCertSwitch
[] = "ignore-certs";
97 const char kServerHostSwitch
[] = "host";
98 const char kServerPortSwitch
[] = "port";
100 void MessageReceivedCallback(const MCSMessage
& message
) {
101 LOG(INFO
) << "Received message with id "
102 << GetPersistentId(message
.GetProtobuf()) << " and tag "
103 << static_cast<int>(message
.tag());
105 if (message
.tag() == kDataMessageStanzaTag
) {
106 const mcs_proto::DataMessageStanza
& data_message
=
107 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
108 message
.GetProtobuf());
109 DVLOG(1) << " to: " << data_message
.to();
110 DVLOG(1) << " from: " << data_message
.from();
111 DVLOG(1) << " category: " << data_message
.category();
112 DVLOG(1) << " sent: " << data_message
.sent();
113 for (int i
= 0; i
< data_message
.app_data_size(); ++i
) {
114 DVLOG(1) << " App data " << i
<< " "
115 << data_message
.app_data(i
).key() << " : "
116 << data_message
.app_data(i
).value();
121 void MessageSentCallback(int64 user_serial_number
,
122 const std::string
& app_id
,
123 const std::string
& message_id
,
124 MCSClient::MessageSendStatus status
) {
125 LOG(INFO
) << "Message sent. Serial number: " << user_serial_number
126 << " Application ID: " << app_id
127 << " Message ID: " << message_id
128 << " Message send status: " << status
;
131 // Needed to use a real host resolver.
132 class MyTestURLRequestContext
: public net::TestURLRequestContext
{
134 MyTestURLRequestContext() : TestURLRequestContext(true) {
135 context_storage_
.set_host_resolver(
136 net::HostResolver::CreateDefaultResolver(NULL
));
137 context_storage_
.set_transport_security_state(
138 new net::TransportSecurityState());
142 virtual ~MyTestURLRequestContext() {}
145 class MyTestURLRequestContextGetter
: public net::TestURLRequestContextGetter
{
147 explicit MyTestURLRequestContextGetter(
148 const scoped_refptr
<base::MessageLoopProxy
>& io_message_loop_proxy
)
149 : TestURLRequestContextGetter(io_message_loop_proxy
) {}
151 virtual net::TestURLRequestContext
* GetURLRequestContext() OVERRIDE
{
152 // Construct |context_| lazily so it gets constructed on the right
153 // thread (the IO thread).
155 context_
.reset(new MyTestURLRequestContext());
156 return context_
.get();
160 virtual ~MyTestURLRequestContextGetter() {}
162 scoped_ptr
<MyTestURLRequestContext
> context_
;
165 // A net log that logs all events by default.
166 class MyTestNetLog
: public net::NetLog
{
169 SetBaseLogLevel(LOG_ALL
);
171 virtual ~MyTestNetLog() {}
174 // A cert verifier that access all certificates.
175 class MyTestCertVerifier
: public net::CertVerifier
{
177 MyTestCertVerifier() {}
178 virtual ~MyTestCertVerifier() {}
180 virtual int Verify(net::X509Certificate
* cert
,
181 const std::string
& hostname
,
183 net::CRLSet
* crl_set
,
184 net::CertVerifyResult
* verify_result
,
185 const net::CompletionCallback
& callback
,
186 RequestHandle
* out_req
,
187 const net::BoundNetLog
& net_log
) OVERRIDE
{
191 virtual void CancelRequest(RequestHandle req
) OVERRIDE
{
199 const CommandLine
& command_line
,
200 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
);
205 uint64
android_id() const { return android_id_
; }
206 uint64
secret() const { return secret_
; }
210 void InitializeNetworkState();
211 void BuildNetworkSession();
213 void LoadCallback(scoped_ptr
<GCMStore::LoadResult
> load_result
);
214 void UpdateCallback(bool success
);
215 void ErrorCallback();
216 void OnCheckInCompleted(
217 const checkin_proto::AndroidCheckinResponse
& checkin_response
);
218 void StartMCSLogin();
220 base::DefaultClock clock_
;
222 CommandLine command_line_
;
224 base::FilePath gcm_store_path_
;
227 std::string server_host_
;
231 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter_
;
232 MyTestNetLog net_log_
;
233 scoped_ptr
<net::NetLogLogger
> logger_
;
234 scoped_ptr
<base::Value
> net_constants_
;
235 scoped_ptr
<net::HostResolver
> host_resolver_
;
236 scoped_ptr
<net::CertVerifier
> cert_verifier_
;
237 scoped_ptr
<net::ServerBoundCertService
> system_server_bound_cert_service_
;
238 scoped_ptr
<net::TransportSecurityState
> transport_security_state_
;
239 scoped_ptr
<net::URLSecurityManager
> url_security_manager_
;
240 scoped_ptr
<net::HttpAuthHandlerFactory
> http_auth_handler_factory_
;
241 scoped_ptr
<net::HttpServerPropertiesImpl
> http_server_properties_
;
242 scoped_ptr
<net::HostMappingRules
> host_mapping_rules_
;
243 scoped_refptr
<net::HttpNetworkSession
> network_session_
;
244 scoped_ptr
<net::ProxyService
> proxy_service_
;
246 GCMStatsRecorder recorder_
;
247 scoped_ptr
<GCMStore
> gcm_store_
;
248 scoped_ptr
<MCSClient
> mcs_client_
;
249 scoped_ptr
<CheckinRequest
> checkin_request_
;
251 scoped_ptr
<ConnectionFactoryImpl
> connection_factory_
;
253 base::Thread file_thread_
;
255 scoped_ptr
<base::RunLoop
> run_loop_
;
259 const CommandLine
& command_line
,
260 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
)
261 : command_line_(command_line
),
262 gcm_store_path_(base::FilePath(FILE_PATH_LITERAL("gcm_store"))),
266 url_request_context_getter_(url_request_context_getter
),
267 file_thread_("FileThread") {
268 if (command_line
.HasSwitch(kRMQFileName
)) {
269 gcm_store_path_
= command_line
.GetSwitchValuePath(kRMQFileName
);
271 if (command_line
.HasSwitch(kAndroidIdSwitch
)) {
272 base::StringToUint64(command_line
.GetSwitchValueASCII(kAndroidIdSwitch
),
275 if (command_line
.HasSwitch(kSecretSwitch
)) {
276 base::StringToUint64(command_line
.GetSwitchValueASCII(kSecretSwitch
),
279 server_host_
= kMCSServerHost
;
280 if (command_line
.HasSwitch(kServerHostSwitch
)) {
281 server_host_
= command_line
.GetSwitchValueASCII(kServerHostSwitch
);
283 server_port_
= kMCSServerPort
;
284 if (command_line
.HasSwitch(kServerPortSwitch
)) {
285 base::StringToInt(command_line
.GetSwitchValueASCII(kServerPortSwitch
),
290 MCSProbe::~MCSProbe() {
294 void MCSProbe::Start() {
295 file_thread_
.Start();
296 InitializeNetworkState();
297 BuildNetworkSession();
298 std::vector
<GURL
> endpoints(1,
300 net::HostPortPair(server_host_
,
301 server_port_
).ToString()));
302 connection_factory_
.reset(
303 new ConnectionFactoryImpl(endpoints
,
304 kDefaultBackoffPolicy
,
309 new GCMStoreImpl(gcm_store_path_
,
310 file_thread_
.message_loop_proxy(),
311 make_scoped_ptr
<Encryptor
>(new FakeEncryptor
)));
312 mcs_client_
.reset(new MCSClient("probe",
314 connection_factory_
.get(),
317 run_loop_
.reset(new base::RunLoop());
318 gcm_store_
->Load(base::Bind(&MCSProbe::LoadCallback
,
319 base::Unretained(this)));
323 void MCSProbe::LoadCallback(scoped_ptr
<GCMStore::LoadResult
> load_result
) {
324 DCHECK(load_result
->success
);
325 if (android_id_
!= 0 && secret_
!= 0) {
326 DVLOG(1) << "Presetting MCS id " << android_id_
;
327 load_result
->device_android_id
= android_id_
;
328 load_result
->device_security_token
= secret_
;
329 gcm_store_
->SetDeviceCredentials(android_id_
,
331 base::Bind(&MCSProbe::UpdateCallback
,
332 base::Unretained(this)));
334 android_id_
= load_result
->device_android_id
;
335 secret_
= load_result
->device_security_token
;
336 DVLOG(1) << "Loaded MCS id " << android_id_
;
338 mcs_client_
->Initialize(
339 base::Bind(&MCSProbe::ErrorCallback
, base::Unretained(this)),
340 base::Bind(&MessageReceivedCallback
),
341 base::Bind(&MessageSentCallback
),
344 if (!android_id_
|| !secret_
) {
345 DVLOG(1) << "Checkin to generate new MCS credentials.";
353 void MCSProbe::UpdateCallback(bool success
) {
356 void MCSProbe::InitializeNetworkState() {
357 FILE* log_file
= NULL
;
358 if (command_line_
.HasSwitch(kLogFileSwitch
)) {
359 base::FilePath log_path
= command_line_
.GetSwitchValuePath(kLogFileSwitch
);
361 log_file
= _wfopen(log_path
.value().c_str(), L
"w");
362 #elif defined(OS_POSIX)
363 log_file
= fopen(log_path
.value().c_str(), "w");
366 net_constants_
.reset(net::NetLogLogger::GetConstants());
367 if (log_file
!= NULL
) {
368 logger_
.reset(new net::NetLogLogger(log_file
, *net_constants_
));
369 logger_
->StartObserving(&net_log_
);
372 host_resolver_
= net::HostResolver::CreateDefaultResolver(&net_log_
);
374 if (command_line_
.HasSwitch(kIgnoreCertSwitch
)) {
375 cert_verifier_
.reset(new MyTestCertVerifier());
377 cert_verifier_
.reset(net::CertVerifier::CreateDefault());
379 system_server_bound_cert_service_
.reset(
380 new net::ServerBoundCertService(
381 new net::DefaultServerBoundCertStore(NULL
),
382 base::WorkerPool::GetTaskRunner(true)));
384 transport_security_state_
.reset(new net::TransportSecurityState());
385 url_security_manager_
.reset(net::URLSecurityManager::Create(NULL
, NULL
));
386 http_auth_handler_factory_
.reset(
387 net::HttpAuthHandlerRegistryFactory::Create(
388 std::vector
<std::string
>(1, "basic"),
389 url_security_manager_
.get(),
390 host_resolver_
.get(),
394 http_server_properties_
.reset(new net::HttpServerPropertiesImpl());
395 host_mapping_rules_
.reset(new net::HostMappingRules());
396 proxy_service_
.reset(net::ProxyService::CreateDirectWithNetLog(&net_log_
));
399 void MCSProbe::BuildNetworkSession() {
400 net::HttpNetworkSession::Params session_params
;
401 session_params
.host_resolver
= host_resolver_
.get();
402 session_params
.cert_verifier
= cert_verifier_
.get();
403 session_params
.server_bound_cert_service
=
404 system_server_bound_cert_service_
.get();
405 session_params
.transport_security_state
= transport_security_state_
.get();
406 session_params
.ssl_config_service
= new net::SSLConfigServiceDefaults();
407 session_params
.http_auth_handler_factory
= http_auth_handler_factory_
.get();
408 session_params
.http_server_properties
=
409 http_server_properties_
->GetWeakPtr();
410 session_params
.network_delegate
= NULL
; // TODO(zea): implement?
411 session_params
.host_mapping_rules
= host_mapping_rules_
.get();
412 session_params
.ignore_certificate_errors
= true;
413 session_params
.http_pipelining_enabled
= false;
414 session_params
.testing_fixed_http_port
= 0;
415 session_params
.testing_fixed_https_port
= 0;
416 session_params
.net_log
= &net_log_
;
417 session_params
.proxy_service
= proxy_service_
.get();
419 network_session_
= new net::HttpNetworkSession(session_params
);
422 void MCSProbe::ErrorCallback() {
423 LOG(INFO
) << "MCS error happened";
426 void MCSProbe::CheckIn() {
427 LOG(INFO
) << "Check-in request initiated.";
428 checkin_proto::ChromeBuildProto chrome_build_proto
;
429 chrome_build_proto
.set_platform(
430 checkin_proto::ChromeBuildProto::PLATFORM_LINUX
);
431 chrome_build_proto
.set_channel(
432 checkin_proto::ChromeBuildProto::CHANNEL_CANARY
);
433 chrome_build_proto
.set_chrome_version(kChromeVersion
);
435 CheckinRequest::RequestInfo
request_info(
436 0, 0, std::string(), std::vector
<std::string
>(), chrome_build_proto
);
438 checkin_request_
.reset(new CheckinRequest(
439 GServicesSettings::DefaultCheckinURL(),
441 kDefaultBackoffPolicy
,
442 base::Bind(&MCSProbe::OnCheckInCompleted
, base::Unretained(this)),
443 url_request_context_getter_
.get(),
445 checkin_request_
->Start();
448 void MCSProbe::OnCheckInCompleted(
449 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
450 bool success
= checkin_response
.has_android_id() &&
451 checkin_response
.android_id() != 0UL &&
452 checkin_response
.has_security_token() &&
453 checkin_response
.security_token() != 0UL;
454 LOG(INFO
) << "Check-in request completion "
455 << (success
? "success!" : "failure!");
460 android_id_
= checkin_response
.android_id();
461 secret_
= checkin_response
.security_token();
463 gcm_store_
->SetDeviceCredentials(android_id_
,
465 base::Bind(&MCSProbe::UpdateCallback
,
466 base::Unretained(this)));
471 void MCSProbe::StartMCSLogin() {
472 LOG(INFO
) << "MCS login initiated.";
474 mcs_client_
->Login(android_id_
, secret_
);
477 int MCSProbeMain(int argc
, char* argv
[]) {
478 base::AtExitManager exit_manager
;
480 CommandLine::Init(argc
, argv
);
481 logging::LoggingSettings settings
;
482 settings
.logging_dest
= logging::LOG_TO_SYSTEM_DEBUG_LOG
;
483 logging::InitLogging(settings
);
485 base::MessageLoopForIO message_loop
;
487 // For check-in and creating registration ids.
488 const scoped_refptr
<MyTestURLRequestContextGetter
> context_getter
=
489 new MyTestURLRequestContextGetter(
490 base::MessageLoop::current()->message_loop_proxy());
492 const CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
494 MCSProbe
mcs_probe(command_line
, context_getter
);
497 base::RunLoop run_loop
;
506 int main(int argc
, char* argv
[]) {
507 return gcm::MCSProbeMain(argc
, argv
);