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. 4// 5// A standalone tool for testing MCS connections and the MCS client on their 6// own. 7 8#include <cstddef> 9#include <cstdio> 10#include <string> 11#include <vector> 12 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/fake_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" 48 49#if defined(OS_MACOSX) 50#include "base/mac/scoped_nsautorelease_pool.h" 51#endif 52 53// This is a simple utility that initializes an mcs client and 54// prints out any events. 55namespace gcm { 56namespace { 57 58const net::BackoffEntry::Policy kDefaultBackoffPolicy = { 59 // Number of initial errors (in sequence) to ignore before applying 60 // exponential back-off rules. 61 0, 62 63 // Initial delay for exponential back-off in ms. 64 15000, // 15 seconds. 65 66 // Factor by which the waiting time will be multiplied. 67 2, 68 69 // Fuzzing percentage. ex: 10% will spread requests randomly 70 // between 90%-100% of the calculated time. 71 0.5, // 50%. 72 73 // Maximum amount of time we are willing to delay our request in ms. 74 1000 * 60 * 5, // 5 minutes. 75 76 // Time to keep an entry from being discarded even when it 77 // has no significant state, -1 to never discard. 78 -1, 79 80 // Don't use initial delay unless the last request was an error. 81 false, 82}; 83 84// Default values used to communicate with the check-in server. 85const char kChromeVersion[] = "Chrome MCS Probe"; 86 87// The default server to communicate with. 88const char kMCSServerHost[] = "mtalk.google.com"; 89const uint16 kMCSServerPort = 5228; 90 91// Command line switches. 92const char kRMQFileName[] = "rmq_file"; 93const char kAndroidIdSwitch[] = "android_id"; 94const char kSecretSwitch[] = "secret"; 95const char kLogFileSwitch[] = "log-file"; 96const char kIgnoreCertSwitch[] = "ignore-certs"; 97const char kServerHostSwitch[] = "host"; 98const char kServerPortSwitch[] = "port"; 99 100void MessageReceivedCallback(const MCSMessage& message) { 101 LOG(INFO) << "Received message with id " 102 << GetPersistentId(message.GetProtobuf()) << " and tag " 103 << static_cast<int>(message.tag()); 104 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(); 117 } 118 } 119} 120 121void 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; 129} 130 131// Needed to use a real host resolver. 132class MyTestURLRequestContext : public net::TestURLRequestContext { 133 public: 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()); 139 Init(); 140 } 141 142 virtual ~MyTestURLRequestContext() {} 143}; 144 145class MyTestURLRequestContextGetter : public net::TestURLRequestContextGetter { 146 public: 147 explicit MyTestURLRequestContextGetter( 148 const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy) 149 : TestURLRequestContextGetter(io_message_loop_proxy) {} 150 151 virtual net::TestURLRequestContext* GetURLRequestContext() OVERRIDE { 152 // Construct |context_| lazily so it gets constructed on the right 153 // thread (the IO thread). 154 if (!context_) 155 context_.reset(new MyTestURLRequestContext()); 156 return context_.get(); 157 } 158 159 private: 160 virtual ~MyTestURLRequestContextGetter() {} 161 162 scoped_ptr<MyTestURLRequestContext> context_; 163}; 164 165// A net log that logs all events by default. 166class MyTestNetLog : public net::NetLog { 167 public: 168 MyTestNetLog() { 169 SetBaseLogLevel(LOG_ALL); 170 } 171 virtual ~MyTestNetLog() {} 172}; 173 174// A cert verifier that access all certificates. 175class MyTestCertVerifier : public net::CertVerifier { 176 public: 177 MyTestCertVerifier() {} 178 virtual ~MyTestCertVerifier() {} 179 180 virtual int Verify(net::X509Certificate* cert, 181 const std::string& hostname, 182 int flags, 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 { 188 return net::OK; 189 } 190 191 virtual void CancelRequest(RequestHandle req) OVERRIDE { 192 // Do nothing. 193 } 194}; 195 196class MCSProbe { 197 public: 198 MCSProbe( 199 const CommandLine& command_line, 200 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter); 201 ~MCSProbe(); 202 203 void Start(); 204 205 uint64 android_id() const { return android_id_; } 206 uint64 secret() const { return secret_; } 207 208 private: 209 void CheckIn(); 210 void InitializeNetworkState(); 211 void BuildNetworkSession(); 212 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(); 219 220 base::DefaultClock clock_; 221 222 CommandLine command_line_; 223 224 base::FilePath gcm_store_path_; 225 uint64 android_id_; 226 uint64 secret_; 227 std::string server_host_; 228 int server_port_; 229 230 // Network state. 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_; 245 246 FakeGCMStatsRecorder recorder_; 247 scoped_ptr<GCMStore> gcm_store_; 248 scoped_ptr<MCSClient> mcs_client_; 249 scoped_ptr<CheckinRequest> checkin_request_; 250 251 scoped_ptr<ConnectionFactoryImpl> connection_factory_; 252 253 base::Thread file_thread_; 254 255 scoped_ptr<base::RunLoop> run_loop_; 256}; 257 258MCSProbe::MCSProbe( 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"))), 263 android_id_(0), 264 secret_(0), 265 server_port_(0), 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); 270 } 271 if (command_line.HasSwitch(kAndroidIdSwitch)) { 272 base::StringToUint64(command_line.GetSwitchValueASCII(kAndroidIdSwitch), 273 &android_id_); 274 } 275 if (command_line.HasSwitch(kSecretSwitch)) { 276 base::StringToUint64(command_line.GetSwitchValueASCII(kSecretSwitch), 277 &secret_); 278 } 279 server_host_ = kMCSServerHost; 280 if (command_line.HasSwitch(kServerHostSwitch)) { 281 server_host_ = command_line.GetSwitchValueASCII(kServerHostSwitch); 282 } 283 server_port_ = kMCSServerPort; 284 if (command_line.HasSwitch(kServerPortSwitch)) { 285 base::StringToInt(command_line.GetSwitchValueASCII(kServerPortSwitch), 286 &server_port_); 287 } 288} 289 290MCSProbe::~MCSProbe() { 291 file_thread_.Stop(); 292} 293 294void MCSProbe::Start() { 295 file_thread_.Start(); 296 InitializeNetworkState(); 297 BuildNetworkSession(); 298 std::vector<GURL> endpoints(1, 299 GURL("https://" + 300 net::HostPortPair(server_host_, 301 server_port_).ToString())); 302 connection_factory_.reset( 303 new ConnectionFactoryImpl(endpoints, 304 kDefaultBackoffPolicy, 305 network_session_, 306 &net_log_, 307 &recorder_)); 308 gcm_store_.reset( 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", 313 &clock_, 314 connection_factory_.get(), 315 gcm_store_.get(), 316 &recorder_)); 317 run_loop_.reset(new base::RunLoop()); 318 gcm_store_->Load(base::Bind(&MCSProbe::LoadCallback, 319 base::Unretained(this))); 320 run_loop_->Run(); 321} 322 323void 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_, 330 secret_, 331 base::Bind(&MCSProbe::UpdateCallback, 332 base::Unretained(this))); 333 } else { 334 android_id_ = load_result->device_android_id; 335 secret_ = load_result->device_security_token; 336 DVLOG(1) << "Loaded MCS id " << android_id_; 337 } 338 mcs_client_->Initialize( 339 base::Bind(&MCSProbe::ErrorCallback, base::Unretained(this)), 340 base::Bind(&MessageReceivedCallback), 341 base::Bind(&MessageSentCallback), 342 load_result.Pass()); 343 344 if (!android_id_ || !secret_) { 345 DVLOG(1) << "Checkin to generate new MCS credentials."; 346 CheckIn(); 347 return; 348 } 349 350 StartMCSLogin(); 351} 352 353void MCSProbe::UpdateCallback(bool success) { 354} 355 356void MCSProbe::InitializeNetworkState() { 357 FILE* log_file = NULL; 358 if (command_line_.HasSwitch(kLogFileSwitch)) { 359 base::FilePath log_path = command_line_.GetSwitchValuePath(kLogFileSwitch); 360#if defined(OS_WIN) 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"); 364#endif 365 } 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_); 370 } 371 372 host_resolver_ = net::HostResolver::CreateDefaultResolver(&net_log_); 373 374 if (command_line_.HasSwitch(kIgnoreCertSwitch)) { 375 cert_verifier_.reset(new MyTestCertVerifier()); 376 } else { 377 cert_verifier_.reset(net::CertVerifier::CreateDefault()); 378 } 379 system_server_bound_cert_service_.reset( 380 new net::ServerBoundCertService( 381 new net::DefaultServerBoundCertStore(NULL), 382 base::WorkerPool::GetTaskRunner(true))); 383 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(), 391 std::string(), 392 false, 393 false)); 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_)); 397} 398 399void 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.testing_fixed_http_port = 0; 414 session_params.testing_fixed_https_port = 0; 415 session_params.net_log = &net_log_; 416 session_params.proxy_service = proxy_service_.get(); 417 418 network_session_ = new net::HttpNetworkSession(session_params); 419} 420 421void MCSProbe::ErrorCallback() { 422 LOG(INFO) << "MCS error happened"; 423} 424 425void MCSProbe::CheckIn() { 426 LOG(INFO) << "Check-in request initiated."; 427 checkin_proto::ChromeBuildProto chrome_build_proto; 428 chrome_build_proto.set_platform( 429 checkin_proto::ChromeBuildProto::PLATFORM_LINUX); 430 chrome_build_proto.set_channel( 431 checkin_proto::ChromeBuildProto::CHANNEL_CANARY); 432 chrome_build_proto.set_chrome_version(kChromeVersion); 433 434 CheckinRequest::RequestInfo request_info( 435 0, 0, std::string(), chrome_build_proto); 436 437 checkin_request_.reset(new CheckinRequest( 438 GServicesSettings::DefaultCheckinURL(), 439 request_info, 440 kDefaultBackoffPolicy, 441 base::Bind(&MCSProbe::OnCheckInCompleted, base::Unretained(this)), 442 url_request_context_getter_.get(), 443 &recorder_)); 444 checkin_request_->Start(); 445} 446 447void MCSProbe::OnCheckInCompleted( 448 const checkin_proto::AndroidCheckinResponse& checkin_response) { 449 bool success = checkin_response.has_android_id() && 450 checkin_response.android_id() != 0UL && 451 checkin_response.has_security_token() && 452 checkin_response.security_token() != 0UL; 453 LOG(INFO) << "Check-in request completion " 454 << (success ? "success!" : "failure!"); 455 456 if (!success) 457 return; 458 459 android_id_ = checkin_response.android_id(); 460 secret_ = checkin_response.security_token(); 461 462 gcm_store_->SetDeviceCredentials(android_id_, 463 secret_, 464 base::Bind(&MCSProbe::UpdateCallback, 465 base::Unretained(this))); 466 467 StartMCSLogin(); 468} 469 470void MCSProbe::StartMCSLogin() { 471 LOG(INFO) << "MCS login initiated."; 472 473 mcs_client_->Login(android_id_, secret_); 474} 475 476int MCSProbeMain(int argc, char* argv[]) { 477 base::AtExitManager exit_manager; 478 479 CommandLine::Init(argc, argv); 480 logging::LoggingSettings settings; 481 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; 482 logging::InitLogging(settings); 483 484 base::MessageLoopForIO message_loop; 485 486 // For check-in and creating registration ids. 487 const scoped_refptr<MyTestURLRequestContextGetter> context_getter = 488 new MyTestURLRequestContextGetter( 489 base::MessageLoop::current()->message_loop_proxy()); 490 491 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 492 493 MCSProbe mcs_probe(command_line, context_getter); 494 mcs_probe.Start(); 495 496 base::RunLoop run_loop; 497 run_loop.Run(); 498 499 return 0; 500} 501 502} // namespace 503} // namespace gcm 504 505int main(int argc, char* argv[]) { 506 return gcm::MCSProbeMain(argc, argv); 507} 508