Home | History | Annotate | Download | only in tools
      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.
     55 namespace gcm {
     56 namespace {
     57 
     58 const 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.
     85 const char kChromeVersion[] = "Chrome MCS Probe";
     86 
     87 // The default server to communicate with.
     88 const char kMCSServerHost[] = "mtalk.google.com";
     89 const uint16 kMCSServerPort = 5228;
     90 
     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";
     99 
    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());
    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 
    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;
    129 }
    130 
    131 // Needed to use a real host resolver.
    132 class 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 
    145 class 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.
    166 class 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.
    175 class 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 
    196 class 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 
    258 MCSProbe::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 
    290 MCSProbe::~MCSProbe() {
    291   file_thread_.Stop();
    292 }
    293 
    294 void 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 
    324 void 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 
    354 void MCSProbe::UpdateCallback(bool success) {
    355 }
    356 
    357 void 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 
    400 void 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 
    421 void MCSProbe::ErrorCallback() {
    422   LOG(INFO) << "MCS error happened";
    423 }
    424 
    425 void 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 
    450 void 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 
    473 void MCSProbe::StartMCSLogin() {
    474   LOG(INFO) << "MCS login initiated.";
    475 
    476   mcs_client_->Login(android_id_, secret_);
    477 }
    478 
    479 int 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 
    508 int main(int argc, char* argv[]) {
    509   return gcm::MCSProbeMain(argc, argv);
    510 }
    511