Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: google_apis/gcm/engine/gservices_settings.cc

Issue 288433002: G-services settings v3 implementation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "google_apis/gcm/engine/gservices_settings.h" 5 #include "google_apis/gcm/engine/gservices_settings.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/sha1.h"
8 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
10 11
11 namespace { 12 namespace {
12 // The expected time in seconds between periodic checkins. 13 // The expected time in seconds between periodic checkins.
13 const char kCheckinIntervalKey[] = "checkin_interval"; 14 const char kCheckinIntervalKey[] = "checkin_interval";
14 // The override URL to the checkin server. 15 // The override URL to the checkin server.
15 const char kCheckinURLKey[] = "checkin_url"; 16 const char kCheckinURLKey[] = "checkin_url";
16 // The MCS machine name to connect to. 17 // The MCS machine name to connect to.
17 const char kMCSHostnameKey[] = "gcm_hostname"; 18 const char kMCSHostnameKey[] = "gcm_hostname";
18 // The MCS port to connect to. 19 // The MCS port to connect to.
19 const char kMCSSecurePortKey[] = "gcm_secure_port"; 20 const char kMCSSecurePortKey[] = "gcm_secure_port";
20 // The URL to get MCS registration IDs. 21 // The URL to get MCS registration IDs.
21 const char kRegistrationURLKey[] = "gcm_registration_url"; 22 const char kRegistrationURLKey[] = "gcm_registration_url";
22 23
23 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days. 24 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days.
24 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours. 25 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours.
25 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin"; 26 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin";
26 const char kDefaultMCSHostname[] = "mtalk.google.com"; 27 const char kDefaultMCSHostname[] = "mtalk.google.com";
27 const int kDefaultMCSMainSecurePort = 5228; 28 const int kDefaultMCSMainSecurePort = 5228;
28 const int kDefaultMCSFallbackSecurePort = 443; 29 const int kDefaultMCSFallbackSecurePort = 443;
29 const char kDefaultRegistrationURL[] = 30 const char kDefaultRegistrationURL[] =
30 "https://android.clients.google.com/c2dm/register3"; 31 "https://android.clients.google.com/c2dm/register3";
32 const char kDeleteSettingPrefix[] = "delete_";
33 const char kDigestVersionPrefix[] = "1-";
31 const char kMCSEnpointTemplate[] = "https://%s:%d"; 34 const char kMCSEnpointTemplate[] = "https://%s:%d";
32 35
33 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) { 36 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
34 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port); 37 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
35 } 38 }
36 39
40 GURL DefaultMainMCSEndpoint() {
41 return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
42 }
43
44 GURL DefaultFallbackMCSEndpoint() {
45 return GURL(
46 MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
47 }
48
37 } // namespace 49 } // namespace
38 50
39 namespace gcm { 51 namespace gcm {
40 52
41 // static 53 // static
42 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() { 54 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() {
43 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval); 55 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval);
44 } 56 }
45 57
46 // static 58 // static
47 const GURL GServicesSettings::DefaultCheckinURL() { 59 const GURL GServicesSettings::DefaultCheckinURL() {
48 return GURL(kDefaultCheckinURL); 60 return GURL(kDefaultCheckinURL);
49 } 61 }
50 62
51 GServicesSettings::GServicesSettings() 63 // static
52 : checkin_interval_(base::TimeDelta::FromSeconds(kDefaultCheckinInterval)), 64 std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
53 checkin_url_(kDefaultCheckinURL), 65 char hash[base::kSHA1Length];
jianli 2014/05/13 21:41:52 Why not defining this as unsigned char array?
fgorski 2014/05/13 23:53:28 Done.
54 mcs_main_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 66 std::vector<char> data;
jianli 2014/05/13 21:41:52 It seems to be easier to use std::string, like:
fgorski 2014/05/13 23:53:28 Done.
55 kDefaultMCSMainSecurePort)), 67 for (SettingsMap::const_iterator iter = settings.begin();
56 mcs_fallback_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 68 iter != settings.end();
57 kDefaultMCSFallbackSecurePort)), 69 ++iter) {
58 registration_url_(kDefaultRegistrationURL), 70 data.insert(data.end(), iter->first.begin(), iter->first.end());
59 weak_ptr_factory_(this) { 71 data.push_back((char)0);
jianli 2014/05/13 21:41:52 nit: '\0' to replace c type conversion
fgorski 2014/05/13 23:53:28 Done.
72 data.insert(data.end(), iter->second.begin(), iter->second.end());
73 data.push_back((char)0);
74 }
75 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(&data[0]),
76 data.size(),
77 reinterpret_cast<unsigned char*>(hash));
78 return kDigestVersionPrefix + base::HexEncode(hash, base::kSHA1Length);
jianli 2014/05/13 21:41:52 Just to confirm that this is not base64 encoded, r
fgorski 2014/05/13 23:53:28 HEX is expected.
79 }
80
81 GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
82 settings_[kCheckinIntervalKey] = base::Int64ToString(kDefaultCheckinInterval);
83 settings_[kCheckinURLKey] = kDefaultCheckinURL;
84 settings_[kMCSHostnameKey] = kDefaultMCSHostname;
85 settings_[kMCSSecurePortKey] = base::IntToString(kDefaultMCSMainSecurePort);
86 settings_[kRegistrationURLKey] = kDefaultRegistrationURL;
87 digest_ = CalculateDigest(settings_);
60 } 88 }
61 89
62 GServicesSettings::~GServicesSettings() {} 90 GServicesSettings::~GServicesSettings() {}
63 91
64 bool GServicesSettings::UpdateFromCheckinResponse( 92 bool GServicesSettings::UpdateFromCheckinResponse(
65 const checkin_proto::AndroidCheckinResponse& checkin_response) { 93 const checkin_proto::AndroidCheckinResponse& checkin_response) {
66 if (!checkin_response.has_digest() || 94 if (!checkin_response.has_settings_diff()) {
67 checkin_response.digest() == digest_) { 95 LOG(ERROR) << "Field settings_diff not set in response.";
68 // There are no changes as digest is the same or no settings provided. 96 return false;
69 return false; 97 }
70 } 98
71 99 if (!checkin_response.has_digest()) {
72 std::map<std::string, std::string> settings; 100 LOG(ERROR) << "Field digest not set in response.";
101 return false;
102 }
103
104 bool settings_diff = checkin_response.settings_diff();
105 SettingsMap new_settings;
106 // Only reuse the existing settings, if we are given a settings difference.
107 if (settings_diff)
108 new_settings = GetSettingsMap();
109
73 for (int i = 0; i < checkin_response.setting_size(); ++i) { 110 for (int i = 0; i < checkin_response.setting_size(); ++i) {
74 std::string name = checkin_response.setting(i).name(); 111 std::string name = checkin_response.setting(i).name();
75 std::string value = checkin_response.setting(i).value(); 112 std::string value = checkin_response.setting(i).value();
76 settings[name] = value; 113 if (settings_diff && name.find(kDeleteSettingPrefix) == 0) {
jianli 2014/05/13 21:41:52 Consider adding the check for empty name.
fgorski 2014/05/13 23:53:28 Done.
77 } 114 std::string setting_to_delete =
78 115 name.substr(arraysize(kDeleteSettingPrefix));
79 // Only update the settings in store and digest, if the settings actually 116 new_settings.erase(setting_to_delete);
80 // passed the verificaiton in update settings. 117 } else {
81 if (UpdateSettings(settings)) { 118 new_settings[name] = value;
82 digest_ = checkin_response.digest(); 119 }
83 return true; 120 }
84 } 121
85 122 if (!VerifySettings(new_settings))
86 return false; 123 return false;
124
125 std::string digest = CalculateDigest(new_settings);
126 if (digest != checkin_response.digest()) {
127 LOG(ERROR) << "Calculated digest does not match the original digest.";
jianli 2014/05/13 21:41:52 nit: combine all 3 logs into one, like: LOG(ERRO
fgorski 2014/05/13 23:53:28 Done.
128 LOG(ERROR) << "Expected G-Services settings digest: "
129 << checkin_response.digest();
130 LOG(ERROR) << "Calculated G-services settings digest: " << digest;
131 return false;
132 }
133
134 if (digest == digest_)
135 return false;
136
137 settings_ = new_settings;
jianli 2014/05/13 21:41:52 nit: use swap for efficiency
fgorski 2014/05/13 23:53:28 Done.
138 digest_ = digest;
139 return true;
87 } 140 }
88 141
89 void GServicesSettings::UpdateFromLoadResult( 142 void GServicesSettings::UpdateFromLoadResult(
90 const GCMStore::LoadResult& load_result) { 143 const GCMStore::LoadResult& load_result) {
91 if (UpdateSettings(load_result.gservices_settings)) 144 if (VerifySettings(load_result.gservices_settings) &&
145 VerifyDigest(load_result.gservices_settings,
146 load_result.gservices_digest)) {
147 settings_ = load_result.gservices_settings;
92 digest_ = load_result.gservices_digest; 148 digest_ = load_result.gservices_digest;
93 } 149 }
94 150 }
95 std::map<std::string, std::string> GServicesSettings::GetSettingsMap() const { 151
96 std::map<std::string, std::string> settings; 152 GServicesSettings::SettingsMap GServicesSettings::GetSettingsMap() const {
jianli 2014/05/13 21:41:52 This can be defined as getter now.
fgorski 2014/05/13 23:53:28 Done.
97 settings[kCheckinIntervalKey] = 153 return settings_;
98 base::Int64ToString(checkin_interval_.InSeconds()); 154 }
99 settings[kCheckinURLKey] = checkin_url_.spec(); 155
100 settings[kMCSHostnameKey] = mcs_main_endpoint_.host(); 156 base::TimeDelta GServicesSettings::checkin_interval() const {
101 settings[kMCSSecurePortKey] = mcs_main_endpoint_.port(); 157 int64 checkin_interval = kMinimumCheckinInterval;
102 settings[kRegistrationURLKey] = registration_url_.spec(); 158 SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
103 return settings; 159 if (iter == settings_.end() ||
104 } 160 !base::StringToInt64(iter->second, &checkin_interval)) {
105 161 checkin_interval = kDefaultCheckinInterval;
106 bool GServicesSettings::UpdateSettings( 162 }
107 const std::map<std::string, std::string>& settings) { 163
108 int64 new_checkin_interval = kMinimumCheckinInterval; 164 if (checkin_interval < kMinimumCheckinInterval)
109 std::map<std::string, std::string>::const_iterator iter = 165 checkin_interval = kMinimumCheckinInterval;
110 settings.find(kCheckinIntervalKey); 166
167 return base::TimeDelta::FromSeconds(checkin_interval);
168 }
169
170 GURL GServicesSettings::checkin_url() const {
171 SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
172 if (iter == settings_.end() || iter->second.empty())
173 return GURL(kDefaultCheckinURL);
174 return GURL(iter->second);
175 }
176
177 GURL GServicesSettings::mcs_main_endpoint() const {
178 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
179 if (iter == settings_.end() || iter->second.empty())
180 return DefaultMainMCSEndpoint();
181
182 std::string mcs_hostname = iter->second;
183
184 iter = settings_.find(kMCSSecurePortKey);
185 int mcs_secure_port = kDefaultMCSMainSecurePort;
186 if (iter == settings_.end() || iter->second.empty() ||
187 !base::StringToInt(iter->second, &mcs_secure_port))
188 return DefaultMainMCSEndpoint();
189
190 GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
191
192 if (!mcs_endpoint.is_valid())
193 return DefaultMainMCSEndpoint();
194
195 return mcs_endpoint;
196 }
197
198 GURL GServicesSettings::mcs_fallback_endpoint() const {
199 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
200 if (iter == settings_.end() || iter->second.empty())
201 return DefaultFallbackMCSEndpoint();
202
203 GURL mcs_endpoint(
204 MakeMCSEndpoint(iter->second, kDefaultMCSFallbackSecurePort));
205
206 if (!mcs_endpoint.is_valid())
207 return DefaultFallbackMCSEndpoint();
208
209 return mcs_endpoint;
210 }
211
212 GURL GServicesSettings::registration_url() const {
213 SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
214 if (iter == settings_.end() || iter->second.empty())
215 return GURL(kDefaultRegistrationURL);
216 return GURL(iter->second);
217 }
218
219 bool GServicesSettings::VerifyCheckinInterval(
220 const SettingsMap& settings) const {
221 SettingsMap::const_iterator iter = settings.find(kCheckinIntervalKey);
111 if (iter == settings.end()) { 222 if (iter == settings.end()) {
112 LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey; 223 LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey;
113 return false; 224 return false;
114 } 225 }
115 if (!base::StringToInt64(iter->second, &new_checkin_interval)) { 226 int64 checkin_interval = kMinimumCheckinInterval;
227 if (!base::StringToInt64(iter->second, &checkin_interval)) {
116 LOG(ERROR) << "Failed to parse checkin interval: " << iter->second; 228 LOG(ERROR) << "Failed to parse checkin interval: " << iter->second;
117 return false; 229 return false;
118 } 230 }
119 if (new_checkin_interval < kMinimumCheckinInterval) { 231 if (checkin_interval == std::numeric_limits<int64>::max()) {
120 LOG(ERROR) << "Checkin interval: " << new_checkin_interval 232 LOG(ERROR) << "Checkin interval is too big: " << checkin_interval;
233 return false;
234 }
235 if (checkin_interval < kMinimumCheckinInterval) {
236 // This is emited as an error, but is not mean to fail verification.
237 LOG(ERROR) << "Checkin interval: " << checkin_interval
121 << " is less than allowed minimum: " << kMinimumCheckinInterval; 238 << " is less than allowed minimum: " << kMinimumCheckinInterval;
122 new_checkin_interval = kMinimumCheckinInterval; 239 }
123 } 240
124 if (new_checkin_interval == std::numeric_limits<int64>::max()) { 241 return true;
125 LOG(ERROR) << "Checkin interval is too big: " << new_checkin_interval; 242 }
126 return false; 243
127 } 244 bool GServicesSettings::VerifyMCSEndpoint(const SettingsMap& settings) const {
128 245 SettingsMap::const_iterator iter = settings.find(kMCSHostnameKey);
129 std::string new_mcs_hostname; 246 std::string mcs_hostname;
130 iter = settings.find(kMCSHostnameKey);
131 if (iter == settings.end()) { 247 if (iter == settings.end()) {
132 LOG(ERROR) << "Setting not found: " << kMCSHostnameKey; 248 LOG(ERROR) << "Setting not found: " << kMCSHostnameKey;
133 return false; 249 return false;
134 } 250 }
135 new_mcs_hostname = iter->second; 251 mcs_hostname = iter->second;
136 if (new_mcs_hostname.empty()) { 252 if (iter->second.empty()) {
137 LOG(ERROR) << "Empty MCS hostname provided."; 253 LOG(ERROR) << "Empty MCS hostname provided.";
138 return false; 254 return false;
139 } 255 }
140 256
141 int new_mcs_secure_port = -1; 257 int mcs_secure_port = -1;
142 iter = settings.find(kMCSSecurePortKey); 258 iter = settings.find(kMCSSecurePortKey);
143 if (iter == settings.end()) { 259 if (iter == settings.end()) {
144 LOG(ERROR) << "Setting not found: " << kMCSSecurePortKey; 260 LOG(ERROR) << "Setting not found: " << kMCSSecurePortKey;
145 return false; 261 return false;
146 } 262 }
147 if (!base::StringToInt(iter->second, &new_mcs_secure_port)) { 263 if (!base::StringToInt(iter->second, &mcs_secure_port)) {
148 LOG(ERROR) << "Failed to parse MCS secure port: " << iter->second; 264 LOG(ERROR) << "Failed to parse MCS secure port: " << iter->second;
149 return false; 265 return false;
150 } 266 }
151 if (new_mcs_secure_port < 0 || 65535 < new_mcs_secure_port) { 267 if (mcs_secure_port < 0 || 65535 < mcs_secure_port) {
152 LOG(ERROR) << "Incorrect port value: " << new_mcs_secure_port; 268 LOG(ERROR) << "Incorrect port value: " << mcs_secure_port;
153 return false; 269 return false;
154 } 270 }
155 271
156 GURL new_mcs_main_endpoint = 272 GURL mcs_main_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
157 GURL(MakeMCSEndpoint(new_mcs_hostname, new_mcs_secure_port)); 273 if (!mcs_main_endpoint.is_valid()) {
158 GURL new_mcs_fallback_endpoint = 274 LOG(ERROR) << "Invalid main MCS endpoint: "
159 GURL(MakeMCSEndpoint(new_mcs_hostname, kDefaultMCSFallbackSecurePort)); 275 << mcs_main_endpoint.possibly_invalid_spec();
160 if (!new_mcs_main_endpoint.is_valid() || 276 return false;
161 !new_mcs_fallback_endpoint.is_valid()) 277 }
162 return false; 278 GURL mcs_fallback_endpoint(
163 279 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
164 GURL new_checkin_url; 280 if (!mcs_fallback_endpoint.is_valid()) {
165 iter = settings.find(kCheckinURLKey); 281 LOG(ERROR) << "Invalid fallback MCS endpoint: "
282 << mcs_fallback_endpoint.possibly_invalid_spec();
283 return false;
284 }
285
286 return true;
287 }
288
289 bool GServicesSettings::VerifyCheckinURL(const SettingsMap& settings) const {
jianli 2014/05/13 21:41:52 It seems that this could be defined as static meth
fgorski 2014/05/13 23:53:28 Done.
290 SettingsMap::const_iterator iter = settings.find(kCheckinURLKey);
166 if (iter == settings.end()) { 291 if (iter == settings.end()) {
167 LOG(ERROR) << "Setting not found: " << kCheckinURLKey; 292 LOG(ERROR) << "Setting not found: " << kCheckinURLKey;
168 return false; 293 return false;
169 } 294 }
170 new_checkin_url = GURL(iter->second); 295 GURL checkin_url(iter->second);
171 if (!new_checkin_url.is_valid()) { 296 if (!checkin_url.is_valid()) {
172 LOG(ERROR) << "Invalid checkin URL provided: " 297 LOG(ERROR) << "Invalid checkin URL provided: "
173 << new_checkin_url.possibly_invalid_spec(); 298 << checkin_url.possibly_invalid_spec();
174 return false; 299 return false;
175 } 300 }
176 301
177 GURL new_registration_url; 302 return true;
178 iter = settings.find(kRegistrationURLKey); 303 }
304
305 bool GServicesSettings::VerifyRegistrationURL(
306 const SettingsMap& settings) const {
307 SettingsMap::const_iterator iter = settings.find(kRegistrationURLKey);
179 if (iter == settings.end()) { 308 if (iter == settings.end()) {
180 LOG(ERROR) << "Setting not found: " << kRegistrationURLKey; 309 LOG(ERROR) << "Setting not found: " << kRegistrationURLKey;
181 return false; 310 return false;
182 } 311 }
183 new_registration_url = GURL(iter->second); 312 GURL registration_url(iter->second);
184 if (!new_registration_url.is_valid()) { 313 if (!registration_url.is_valid()) {
185 LOG(ERROR) << "Invalid registration URL provided: " 314 LOG(ERROR) << "Invalid registration URL provided: "
186 << new_registration_url.possibly_invalid_spec(); 315 << registration_url.possibly_invalid_spec();
187 return false; 316 return false;
188 } 317 }
189 318
190 // We only update the settings once all of them are correct. 319 return true;
191 checkin_interval_ = base::TimeDelta::FromSeconds(new_checkin_interval); 320 }
192 mcs_main_endpoint_ = new_mcs_main_endpoint; 321
193 mcs_fallback_endpoint_ = new_mcs_fallback_endpoint; 322 bool GServicesSettings::VerifySettings(const SettingsMap& settings) const {
194 checkin_url_ = new_checkin_url; 323 return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
195 registration_url_ = new_registration_url; 324 VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
196 return true; 325 }
197 } 326
198 327 bool GServicesSettings::VerifyDigest(const SettingsMap& settings,
328 const std::string& expected_digest) const {
329 std::string calculated_digest = CalculateDigest(settings);
330 if (calculated_digest != expected_digest) {
331 LOG(ERROR) << "Calculated digest does not match the original digest.";
332 LOG(ERROR) << "Expected G-Services settings digest: " << expected_digest;
333 LOG(ERROR) << "Calculated G-services settings digest: "
334 << calculated_digest;
335 return false;
336 }
337
338 return true;
339 }
340
199 } // namespace gcm 341 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698