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