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
12#include "base/at_exit.h"
13#include "base/command_line.h"
14#include "base/compiler_specific.h"
15#include "base/logging.h"
16#include "base/memory/ref_counted.h"
17#include "base/memory/scoped_ptr.h"
18#include "base/message_loop/message_loop.h"
19#include "base/run_loop.h"
20#include "base/strings/string_number_conversions.h"
21#include "base/threading/thread.h"
22#include "base/threading/worker_pool.h"
23#include "base/values.h"
24#include "google_apis/gcm/base/mcs_message.h"
25#include "google_apis/gcm/base/mcs_util.h"
26#include "google_apis/gcm/engine/connection_factory_impl.h"
27#include "google_apis/gcm/engine/mcs_client.h"
28#include "net/base/host_mapping_rules.h"
29#include "net/base/net_log_logger.h"
30#include "net/cert/cert_verifier.h"
31#include "net/dns/host_resolver.h"
32#include "net/http/http_auth_handler_factory.h"
33#include "net/http/http_network_session.h"
34#include "net/http/http_server_properties_impl.h"
35#include "net/http/transport_security_state.h"
36#include "net/socket/client_socket_factory.h"
37#include "net/socket/ssl_client_socket.h"
38#include "net/ssl/default_server_bound_cert_store.h"
39#include "net/ssl/server_bound_cert_service.h"
40#include "net/url_request/url_request_test_util.h"
41
42#if defined(OS_MACOSX)
43#include "base/mac/scoped_nsautorelease_pool.h"
44#endif
45
46// This is a simple utility that initializes an mcs client and
47// prints out any events.
48namespace gcm {
49namespace {
50
51// The default server to communicate with.
52const char kMCSServerHost[] = "mtalk.google.com";
53const uint16 kMCSServerPort = 5228;
54
55// Command line switches.
56const char kRMQFileName[] = "rmq_file";
57const char kAndroidIdSwitch[] = "android_id";
58const char kSecretSwitch[] = "secret";
59const char kLogFileSwitch[] = "log-file";
60const char kIgnoreCertSwitch[] = "ignore-certs";
61const char kServerHostSwitch[] = "host";
62const char kServerPortSwitch[] = "port";
63
64void MessageReceivedCallback(const MCSMessage& message) {
65  LOG(INFO) << "Received message with id "
66            << GetPersistentId(message.GetProtobuf()) << " and tag "
67            << static_cast<int>(message.tag());
68
69  if (message.tag() == kDataMessageStanzaTag) {
70    const mcs_proto::DataMessageStanza& data_message =
71        reinterpret_cast<const mcs_proto::DataMessageStanza&>(
72            message.GetProtobuf());
73    DVLOG(1) << "  to: " << data_message.to();
74    DVLOG(1) << "  from: " << data_message.from();
75    DVLOG(1) << "  category: " << data_message.category();
76    DVLOG(1) << "  sent: " << data_message.sent();
77    for (int i = 0; i < data_message.app_data_size(); ++i) {
78      DVLOG(1) << "  App data " << i << " "
79               << data_message.app_data(i).key() << " : "
80               << data_message.app_data(i).value();
81    }
82  }
83}
84
85void MessageSentCallback(const std::string& local_id) {
86  LOG(INFO) << "Message sent. Status: " << local_id;
87}
88
89// Needed to use a real host resolver.
90class MyTestURLRequestContext : public net::TestURLRequestContext {
91 public:
92  MyTestURLRequestContext() : TestURLRequestContext(true) {
93    context_storage_.set_host_resolver(
94        net::HostResolver::CreateDefaultResolver(NULL));
95    context_storage_.set_transport_security_state(
96        new net::TransportSecurityState());
97    Init();
98  }
99
100  virtual ~MyTestURLRequestContext() {}
101};
102
103class MyTestURLRequestContextGetter : public net::TestURLRequestContextGetter {
104 public:
105  explicit MyTestURLRequestContextGetter(
106      const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy)
107      : TestURLRequestContextGetter(io_message_loop_proxy) {}
108
109  virtual net::TestURLRequestContext* GetURLRequestContext() OVERRIDE {
110    // Construct |context_| lazily so it gets constructed on the right
111    // thread (the IO thread).
112    if (!context_)
113      context_.reset(new MyTestURLRequestContext());
114    return context_.get();
115  }
116
117 private:
118  virtual ~MyTestURLRequestContextGetter() {}
119
120  scoped_ptr<MyTestURLRequestContext> context_;
121};
122
123// A net log that logs all events by default.
124class MyTestNetLog : public net::NetLog {
125 public:
126  MyTestNetLog() {
127    SetBaseLogLevel(LOG_ALL);
128  }
129  virtual ~MyTestNetLog() {}
130};
131
132// A cert verifier that access all certificates.
133class MyTestCertVerifier : public net::CertVerifier {
134 public:
135  MyTestCertVerifier() {}
136  virtual ~MyTestCertVerifier() {}
137
138  virtual int Verify(net::X509Certificate* cert,
139                     const std::string& hostname,
140                     int flags,
141                     net::CRLSet* crl_set,
142                     net::CertVerifyResult* verify_result,
143                     const net::CompletionCallback& callback,
144                     RequestHandle* out_req,
145                     const net::BoundNetLog& net_log) OVERRIDE {
146    return net::OK;
147  }
148
149  virtual void CancelRequest(RequestHandle req) OVERRIDE {
150    // Do nothing.
151  }
152};
153
154class MCSProbe {
155 public:
156  MCSProbe(
157      const CommandLine& command_line,
158      scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
159  ~MCSProbe();
160
161  void Start();
162
163  uint64 android_id() const { return android_id_; }
164  uint64 secret() const { return secret_; }
165
166 private:
167  void InitializeNetworkState();
168  void BuildNetworkSession();
169
170  void InitializationCallback(bool success,
171                              uint64 restored_android_id,
172                              uint64 restored_security_token);
173
174  CommandLine command_line_;
175
176  base::FilePath rmq_path_;
177  uint64 android_id_;
178  uint64 secret_;
179  std::string server_host_;
180  int server_port_;
181
182  // Network state.
183  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
184  MyTestNetLog net_log_;
185  scoped_ptr<net::NetLogLogger> logger_;
186  scoped_ptr<base::Value> net_constants_;
187  scoped_ptr<net::HostResolver> host_resolver_;
188  scoped_ptr<net::CertVerifier> cert_verifier_;
189  scoped_ptr<net::ServerBoundCertService> system_server_bound_cert_service_;
190  scoped_ptr<net::TransportSecurityState> transport_security_state_;
191  scoped_ptr<net::URLSecurityManager> url_security_manager_;
192  scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory_;
193  scoped_ptr<net::HttpServerPropertiesImpl> http_server_properties_;
194  scoped_ptr<net::HostMappingRules> host_mapping_rules_;
195  scoped_refptr<net::HttpNetworkSession> network_session_;
196  scoped_ptr<net::ProxyService> proxy_service_;
197
198  scoped_ptr<MCSClient> mcs_client_;
199
200  scoped_ptr<ConnectionFactoryImpl> connection_factory_;
201
202  base::Thread file_thread_;
203
204  scoped_ptr<base::RunLoop> run_loop_;
205};
206
207MCSProbe::MCSProbe(
208    const CommandLine& command_line,
209    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter)
210    : command_line_(command_line),
211      rmq_path_(base::FilePath(FILE_PATH_LITERAL("gcm_rmq_store"))),
212      android_id_(0),
213      secret_(0),
214      server_port_(0),
215      url_request_context_getter_(url_request_context_getter),
216      file_thread_("FileThread") {
217  if (command_line.HasSwitch(kRMQFileName)) {
218    rmq_path_ = command_line.GetSwitchValuePath(kRMQFileName);
219  }
220  if (command_line.HasSwitch(kAndroidIdSwitch)) {
221    base::StringToUint64(command_line.GetSwitchValueASCII(kAndroidIdSwitch),
222                         &android_id_);
223  }
224  if (command_line.HasSwitch(kSecretSwitch)) {
225    base::StringToUint64(command_line.GetSwitchValueASCII(kSecretSwitch),
226                         &secret_);
227  }
228  server_host_ = kMCSServerHost;
229  if (command_line.HasSwitch(kServerHostSwitch)) {
230    server_host_ = command_line.GetSwitchValueASCII(kServerHostSwitch);
231  }
232  server_port_ = kMCSServerPort;
233  if (command_line.HasSwitch(kServerPortSwitch)) {
234    base::StringToInt(command_line.GetSwitchValueASCII(kServerPortSwitch),
235                      &server_port_);
236  }
237}
238
239MCSProbe::~MCSProbe() {
240  file_thread_.Stop();
241}
242
243void MCSProbe::Start() {
244  file_thread_.Start();
245  InitializeNetworkState();
246  BuildNetworkSession();
247  connection_factory_.reset(
248      new ConnectionFactoryImpl(GURL("https://" + net::HostPortPair(
249                                    server_host_, server_port_).ToString()),
250                                network_session_,
251                                &net_log_));
252  mcs_client_.reset(new MCSClient(rmq_path_,
253                                  connection_factory_.get(),
254                                  file_thread_.message_loop_proxy()));
255  run_loop_.reset(new base::RunLoop());
256  mcs_client_->Initialize(base::Bind(&MCSProbe::InitializationCallback,
257                                     base::Unretained(this)),
258                          base::Bind(&MessageReceivedCallback),
259                          base::Bind(&MessageSentCallback));
260  run_loop_->Run();
261}
262
263void MCSProbe::InitializeNetworkState() {
264  FILE* log_file = NULL;
265  if (command_line_.HasSwitch(kLogFileSwitch)) {
266    base::FilePath log_path = command_line_.GetSwitchValuePath(kLogFileSwitch);
267#if defined(OS_WIN)
268    log_file = _wfopen(log_path.value().c_str(), L"w");
269#elif defined(OS_POSIX)
270    log_file = fopen(log_path.value().c_str(), "w");
271#endif
272  }
273  net_constants_.reset(net::NetLogLogger::GetConstants());
274  if (log_file != NULL) {
275    logger_.reset(new net::NetLogLogger(log_file, *net_constants_));
276    logger_->StartObserving(&net_log_);
277  }
278
279  host_resolver_ = net::HostResolver::CreateDefaultResolver(&net_log_);
280
281  if (command_line_.HasSwitch(kIgnoreCertSwitch)) {
282    cert_verifier_.reset(new MyTestCertVerifier());
283  } else {
284    cert_verifier_.reset(net::CertVerifier::CreateDefault());
285  }
286  system_server_bound_cert_service_.reset(
287      new net::ServerBoundCertService(
288          new net::DefaultServerBoundCertStore(NULL),
289          base::WorkerPool::GetTaskRunner(true)));
290
291  transport_security_state_.reset(new net::TransportSecurityState());
292  url_security_manager_.reset(net::URLSecurityManager::Create(NULL, NULL));
293  http_auth_handler_factory_.reset(
294      net::HttpAuthHandlerRegistryFactory::Create(
295          std::vector<std::string>(1, "basic"),
296          url_security_manager_.get(),
297          host_resolver_.get(),
298          std::string(),
299          false,
300          false));
301  http_server_properties_.reset(new net::HttpServerPropertiesImpl());
302  host_mapping_rules_.reset(new net::HostMappingRules());
303  proxy_service_.reset(net::ProxyService::CreateDirectWithNetLog(&net_log_));
304}
305
306void MCSProbe::BuildNetworkSession() {
307  net::HttpNetworkSession::Params session_params;
308  session_params.host_resolver = host_resolver_.get();
309  session_params.cert_verifier = cert_verifier_.get();
310  session_params.server_bound_cert_service =
311      system_server_bound_cert_service_.get();
312  session_params.transport_security_state = transport_security_state_.get();
313  session_params.ssl_config_service = new net::SSLConfigServiceDefaults();
314  session_params.http_auth_handler_factory = http_auth_handler_factory_.get();
315  session_params.http_server_properties =
316      http_server_properties_->GetWeakPtr();
317  session_params.network_delegate = NULL;  // TODO(zea): implement?
318  session_params.host_mapping_rules = host_mapping_rules_.get();
319  session_params.ignore_certificate_errors = true;
320  session_params.http_pipelining_enabled = false;
321  session_params.testing_fixed_http_port = 0;
322  session_params.testing_fixed_https_port = 0;
323  session_params.net_log = &net_log_;
324  session_params.proxy_service = proxy_service_.get();
325
326  network_session_ = new net::HttpNetworkSession(session_params);
327}
328
329void MCSProbe::InitializationCallback(bool success,
330                                      uint64 restored_android_id,
331                                      uint64 restored_security_token) {
332  LOG(INFO) << "Initialization " << (success ? "success!" : "failure!");
333  if (restored_android_id && restored_security_token) {
334    android_id_ = restored_android_id;
335    secret_ = restored_security_token;
336  }
337  if (success)
338    mcs_client_->Login(android_id_, secret_);
339}
340
341int MCSProbeMain(int argc, char* argv[]) {
342  base::AtExitManager exit_manager;
343
344  CommandLine::Init(argc, argv);
345  logging::LoggingSettings settings;
346  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
347  logging::InitLogging(settings);
348
349  base::MessageLoopForIO message_loop;
350
351  // For check-in and creating registration ids.
352  const scoped_refptr<MyTestURLRequestContextGetter> context_getter =
353      new MyTestURLRequestContextGetter(
354          base::MessageLoop::current()->message_loop_proxy());
355
356  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
357
358  MCSProbe mcs_probe(command_line, context_getter);
359  mcs_probe.Start();
360
361  base::RunLoop run_loop;
362  run_loop.Run();
363
364  return 0;
365}
366
367}  // namespace
368}  // namespace gcm
369
370int main(int argc, char* argv[]) {
371  return gcm::MCSProbeMain(argc, argv);
372}
373