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/channel_id_service.h" 46#include "net/ssl/default_channel_id_store.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::ChannelIDService> system_channel_id_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 NULL, 307 &net_log_, 308 &recorder_)); 309 gcm_store_.reset( 310 new GCMStoreImpl(gcm_store_path_, 311 file_thread_.message_loop_proxy(), 312 make_scoped_ptr<Encryptor>(new FakeEncryptor))); 313 mcs_client_.reset(new MCSClient("probe", 314 &clock_, 315 connection_factory_.get(), 316 gcm_store_.get(), 317 &recorder_)); 318 run_loop_.reset(new base::RunLoop()); 319 gcm_store_->Load(base::Bind(&MCSProbe::LoadCallback, 320 base::Unretained(this))); 321 run_loop_->Run(); 322} 323 324void MCSProbe::LoadCallback(scoped_ptr<GCMStore::LoadResult> load_result) { 325 DCHECK(load_result->success); 326 if (android_id_ != 0 && secret_ != 0) { 327 DVLOG(1) << "Presetting MCS id " << android_id_; 328 load_result->device_android_id = android_id_; 329 load_result->device_security_token = secret_; 330 gcm_store_->SetDeviceCredentials(android_id_, 331 secret_, 332 base::Bind(&MCSProbe::UpdateCallback, 333 base::Unretained(this))); 334 } else { 335 android_id_ = load_result->device_android_id; 336 secret_ = load_result->device_security_token; 337 DVLOG(1) << "Loaded MCS id " << android_id_; 338 } 339 mcs_client_->Initialize( 340 base::Bind(&MCSProbe::ErrorCallback, base::Unretained(this)), 341 base::Bind(&MessageReceivedCallback), 342 base::Bind(&MessageSentCallback), 343 load_result.Pass()); 344 345 if (!android_id_ || !secret_) { 346 DVLOG(1) << "Checkin to generate new MCS credentials."; 347 CheckIn(); 348 return; 349 } 350 351 StartMCSLogin(); 352} 353 354void MCSProbe::UpdateCallback(bool success) { 355} 356 357void MCSProbe::InitializeNetworkState() { 358 FILE* log_file = NULL; 359 if (command_line_.HasSwitch(kLogFileSwitch)) { 360 base::FilePath log_path = command_line_.GetSwitchValuePath(kLogFileSwitch); 361#if defined(OS_WIN) 362 log_file = _wfopen(log_path.value().c_str(), L"w"); 363#elif defined(OS_POSIX) 364 log_file = fopen(log_path.value().c_str(), "w"); 365#endif 366 } 367 net_constants_.reset(net::NetLogLogger::GetConstants()); 368 if (log_file != NULL) { 369 logger_.reset(new net::NetLogLogger(log_file, *net_constants_)); 370 logger_->StartObserving(&net_log_); 371 } 372 373 host_resolver_ = net::HostResolver::CreateDefaultResolver(&net_log_); 374 375 if (command_line_.HasSwitch(kIgnoreCertSwitch)) { 376 cert_verifier_.reset(new MyTestCertVerifier()); 377 } else { 378 cert_verifier_.reset(net::CertVerifier::CreateDefault()); 379 } 380 system_channel_id_service_.reset( 381 new net::ChannelIDService( 382 new net::DefaultChannelIDStore(NULL), 383 base::WorkerPool::GetTaskRunner(true))); 384 385 transport_security_state_.reset(new net::TransportSecurityState()); 386 url_security_manager_.reset(net::URLSecurityManager::Create(NULL, NULL)); 387 http_auth_handler_factory_.reset( 388 net::HttpAuthHandlerRegistryFactory::Create( 389 std::vector<std::string>(1, "basic"), 390 url_security_manager_.get(), 391 host_resolver_.get(), 392 std::string(), 393 false, 394 false)); 395 http_server_properties_.reset(new net::HttpServerPropertiesImpl()); 396 host_mapping_rules_.reset(new net::HostMappingRules()); 397 proxy_service_.reset(net::ProxyService::CreateDirectWithNetLog(&net_log_)); 398} 399 400void MCSProbe::BuildNetworkSession() { 401 net::HttpNetworkSession::Params session_params; 402 session_params.host_resolver = host_resolver_.get(); 403 session_params.cert_verifier = cert_verifier_.get(); 404 session_params.channel_id_service = system_channel_id_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(0, 435 0, 436 std::map<std::string, std::string>(), 437 std::string(), 438 chrome_build_proto); 439 440 checkin_request_.reset(new CheckinRequest( 441 GServicesSettings::DefaultCheckinURL(), 442 request_info, 443 kDefaultBackoffPolicy, 444 base::Bind(&MCSProbe::OnCheckInCompleted, base::Unretained(this)), 445 url_request_context_getter_.get(), 446 &recorder_)); 447 checkin_request_->Start(); 448} 449 450void MCSProbe::OnCheckInCompleted( 451 const checkin_proto::AndroidCheckinResponse& checkin_response) { 452 bool success = checkin_response.has_android_id() && 453 checkin_response.android_id() != 0UL && 454 checkin_response.has_security_token() && 455 checkin_response.security_token() != 0UL; 456 LOG(INFO) << "Check-in request completion " 457 << (success ? "success!" : "failure!"); 458 459 if (!success) 460 return; 461 462 android_id_ = checkin_response.android_id(); 463 secret_ = checkin_response.security_token(); 464 465 gcm_store_->SetDeviceCredentials(android_id_, 466 secret_, 467 base::Bind(&MCSProbe::UpdateCallback, 468 base::Unretained(this))); 469 470 StartMCSLogin(); 471} 472 473void MCSProbe::StartMCSLogin() { 474 LOG(INFO) << "MCS login initiated."; 475 476 mcs_client_->Login(android_id_, secret_); 477} 478 479int MCSProbeMain(int argc, char* argv[]) { 480 base::AtExitManager exit_manager; 481 482 CommandLine::Init(argc, argv); 483 logging::LoggingSettings settings; 484 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; 485 logging::InitLogging(settings); 486 487 base::MessageLoopForIO message_loop; 488 489 // For check-in and creating registration ids. 490 const scoped_refptr<MyTestURLRequestContextGetter> context_getter = 491 new MyTestURLRequestContextGetter( 492 base::MessageLoop::current()->message_loop_proxy()); 493 494 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 495 496 MCSProbe mcs_probe(command_line, context_getter); 497 mcs_probe.Start(); 498 499 base::RunLoop run_loop; 500 run_loop.Run(); 501 502 return 0; 503} 504 505} // namespace 506} // namespace gcm 507 508int main(int argc, char* argv[]) { 509 return gcm::MCSProbeMain(argc, argv); 510} 511