• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MessageReceivedCallback(const MCSMessage & message)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 
MessageSentCallback(int64 user_serial_number,const std::string & app_id,const std::string & message_id,MCSClient::MessageSendStatus status)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:
MyTestURLRequestContext()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 
~MyTestURLRequestContext()142   virtual ~MyTestURLRequestContext() {}
143 };
144 
145 class MyTestURLRequestContextGetter : public net::TestURLRequestContextGetter {
146  public:
MyTestURLRequestContextGetter(const scoped_refptr<base::MessageLoopProxy> & io_message_loop_proxy)147   explicit MyTestURLRequestContextGetter(
148       const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy)
149       : TestURLRequestContextGetter(io_message_loop_proxy) {}
150 
GetURLRequestContext()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:
~MyTestURLRequestContextGetter()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:
MyTestNetLog()168   MyTestNetLog() {
169     SetBaseLogLevel(LOG_ALL);
170   }
~MyTestNetLog()171   virtual ~MyTestNetLog() {}
172 };
173 
174 // A cert verifier that access all certificates.
175 class MyTestCertVerifier : public net::CertVerifier {
176  public:
MyTestCertVerifier()177   MyTestCertVerifier() {}
~MyTestCertVerifier()178   virtual ~MyTestCertVerifier() {}
179 
Verify(net::X509Certificate * cert,const std::string & hostname,int flags,net::CRLSet * crl_set,net::CertVerifyResult * verify_result,const net::CompletionCallback & callback,RequestHandle * out_req,const net::BoundNetLog & net_log)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 
CancelRequest(RequestHandle req)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 
android_id() const205   uint64 android_id() const { return android_id_; }
secret() const206   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 
MCSProbe(const CommandLine & command_line,scoped_refptr<net::URLRequestContextGetter> url_request_context_getter)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 
~MCSProbe()290 MCSProbe::~MCSProbe() {
291   file_thread_.Stop();
292 }
293 
Start()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 
LoadCallback(scoped_ptr<GCMStore::LoadResult> load_result)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 
UpdateCallback(bool success)354 void MCSProbe::UpdateCallback(bool success) {
355 }
356 
InitializeNetworkState()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 
BuildNetworkSession()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 
ErrorCallback()421 void MCSProbe::ErrorCallback() {
422   LOG(INFO) << "MCS error happened";
423 }
424 
CheckIn()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 
OnCheckInCompleted(const checkin_proto::AndroidCheckinResponse & checkin_response)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 
StartMCSLogin()473 void MCSProbe::StartMCSLogin() {
474   LOG(INFO) << "MCS login initiated.";
475 
476   mcs_client_->Login(android_id_, secret_);
477 }
478 
MCSProbeMain(int argc,char * argv[])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 
main(int argc,char * argv[])508 int main(int argc, char* argv[]) {
509   return gcm::MCSProbeMain(argc, argv);
510 }
511