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

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: Adding tests for settings diff, addressing CR feedback" 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_";
Nicolas Zea 2014/05/14 23:00:33 nit: comment what these are for?
fgorski 2014/05/16 20:48:47 Done.
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
49 bool VerifyCheckinInterval(
50 const gcm::GServicesSettings::SettingsMap& settings) {
51 gcm::GServicesSettings::SettingsMap::const_iterator iter =
52 settings.find(kCheckinIntervalKey);
53 if (iter == settings.end()) {
54 LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey;
55 return false;
56 }
57 int64 checkin_interval = kMinimumCheckinInterval;
58 if (!base::StringToInt64(iter->second, &checkin_interval)) {
59 LOG(ERROR) << "Failed to parse checkin interval: " << iter->second;
60 return false;
61 }
62 if (checkin_interval == std::numeric_limits<int64>::max()) {
63 LOG(ERROR) << "Checkin interval is too big: " << checkin_interval;
64 return false;
65 }
66 if (checkin_interval < kMinimumCheckinInterval) {
67 // This is emited as an error, but is not meant to fail verification.
68 LOG(ERROR) << "Checkin interval: " << checkin_interval
69 << " is less than allowed minimum: " << kMinimumCheckinInterval;
70 }
71
72 return true;
73 }
74
75 bool VerifyMCSEndpoint(const gcm::GServicesSettings::SettingsMap& settings) {
76 gcm::GServicesSettings::SettingsMap::const_iterator iter =
77 settings.find(kMCSHostnameKey);
78 std::string mcs_hostname;
79 if (iter == settings.end()) {
80 LOG(ERROR) << "Setting not found: " << kMCSHostnameKey;
81 return false;
82 }
83 mcs_hostname = iter->second;
84 if (iter->second.empty()) {
85 LOG(ERROR) << "Empty MCS hostname provided.";
86 return false;
87 }
88
89 int mcs_secure_port = -1;
90 iter = settings.find(kMCSSecurePortKey);
91 if (iter == settings.end()) {
92 LOG(ERROR) << "Setting not found: " << kMCSSecurePortKey;
93 return false;
94 }
95 if (!base::StringToInt(iter->second, &mcs_secure_port)) {
96 LOG(ERROR) << "Failed to parse MCS secure port: " << iter->second;
97 return false;
98 }
99 if (mcs_secure_port < 0 || 65535 < mcs_secure_port) {
Nicolas Zea 2014/05/14 23:00:33 nit: mcs_secure_port > 65535 is more readable/cons
fgorski 2014/05/16 20:48:47 Done.
100 LOG(ERROR) << "Incorrect port value: " << mcs_secure_port;
101 return false;
102 }
103
104 GURL mcs_main_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
105 if (!mcs_main_endpoint.is_valid()) {
106 LOG(ERROR) << "Invalid main MCS endpoint: "
107 << mcs_main_endpoint.possibly_invalid_spec();
108 return false;
109 }
110 GURL mcs_fallback_endpoint(
111 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
112 if (!mcs_fallback_endpoint.is_valid()) {
113 LOG(ERROR) << "Invalid fallback MCS endpoint: "
114 << mcs_fallback_endpoint.possibly_invalid_spec();
115 return false;
116 }
117
118 return true;
119 }
120
121 bool VerifyCheckinURL(const gcm::GServicesSettings::SettingsMap& settings) {
122 gcm::GServicesSettings::SettingsMap::const_iterator iter =
123 settings.find(kCheckinURLKey);
124 if (iter == settings.end()) {
125 LOG(ERROR) << "Setting not found: " << kCheckinURLKey;
126 return false;
127 }
128 GURL checkin_url(iter->second);
129 if (!checkin_url.is_valid()) {
130 LOG(ERROR) << "Invalid checkin URL provided: " << iter->second;
131 return false;
132 }
133
134 return true;
135 }
136
137 bool VerifyRegistrationURL(
138 const gcm::GServicesSettings::SettingsMap& settings) {
139 gcm::GServicesSettings::SettingsMap::const_iterator iter =
140 settings.find(kRegistrationURLKey);
141 if (iter == settings.end()) {
142 LOG(ERROR) << "Setting not found: " << kRegistrationURLKey;
143 return false;
144 }
145 GURL registration_url(iter->second);
146 if (!registration_url.is_valid()) {
147 LOG(ERROR) << "Invalid registration URL provided: " << iter->second;
148 return false;
149 }
150
151 return true;
152 }
153
154 bool VerifySettings(const gcm::GServicesSettings::SettingsMap& settings) {
155 return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
156 VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
157 }
158
159 bool VerifyDigest(const gcm::GServicesSettings::SettingsMap& settings,
160 const std::string& expected_digest) {
161 std::string calculated_digest =
162 gcm::GServicesSettings::CalculateDigest(settings);
163 if (calculated_digest != expected_digest) {
164 LOG(ERROR) << "Calculated digest does not match the original digest.";
165 LOG(ERROR) << "Expected G-Services settings digest: " << expected_digest;
166 LOG(ERROR) << "Calculated G-services settings digest: "
167 << calculated_digest;
168 return false;
169 }
170
171 return true;
172 }
173
37 } // namespace 174 } // namespace
38 175
39 namespace gcm { 176 namespace gcm {
40 177
41 // static 178 // static
42 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() { 179 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() {
43 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval); 180 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval);
44 } 181 }
45 182
46 // static 183 // static
47 const GURL GServicesSettings::DefaultCheckinURL() { 184 const GURL GServicesSettings::DefaultCheckinURL() {
48 return GURL(kDefaultCheckinURL); 185 return GURL(kDefaultCheckinURL);
49 } 186 }
50 187
51 GServicesSettings::GServicesSettings() 188 // static
52 : checkin_interval_(base::TimeDelta::FromSeconds(kDefaultCheckinInterval)), 189 std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
53 checkin_url_(kDefaultCheckinURL), 190 unsigned char hash[base::kSHA1Length];
54 mcs_main_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 191 std::string data;
55 kDefaultMCSMainSecurePort)), 192 for (SettingsMap::const_iterator iter = settings.begin();
56 mcs_fallback_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 193 iter != settings.end();
57 kDefaultMCSFallbackSecurePort)), 194 ++iter) {
58 registration_url_(kDefaultRegistrationURL), 195 data += iter->first;
59 weak_ptr_factory_(this) { 196 data += '\0';
60 } 197 data += iter->second;
61 198 data += '\0';
62 GServicesSettings::~GServicesSettings() {} 199 }
200 base::SHA1HashBytes(
201 reinterpret_cast<const unsigned char*>(&data[0]), data.size(), hash);
202 return kDigestVersionPrefix + base::HexEncode(hash, base::kSHA1Length);
203 }
204
205 GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
206 settings_[kCheckinIntervalKey] = base::Int64ToString(kDefaultCheckinInterval);
207 settings_[kCheckinURLKey] = kDefaultCheckinURL;
208 settings_[kMCSHostnameKey] = kDefaultMCSHostname;
209 settings_[kMCSSecurePortKey] = base::IntToString(kDefaultMCSMainSecurePort);
210 settings_[kRegistrationURLKey] = kDefaultRegistrationURL;
211 digest_ = CalculateDigest(settings_);
212 }
213
214 GServicesSettings::~GServicesSettings() {
215 }
63 216
64 bool GServicesSettings::UpdateFromCheckinResponse( 217 bool GServicesSettings::UpdateFromCheckinResponse(
65 const checkin_proto::AndroidCheckinResponse& checkin_response) { 218 const checkin_proto::AndroidCheckinResponse& checkin_response) {
66 if (!checkin_response.has_digest() || 219 if (!checkin_response.has_settings_diff()) {
67 checkin_response.digest() == digest_) { 220 LOG(ERROR) << "Field settings_diff not set in response.";
Nicolas Zea 2014/05/14 23:00:33 will settings_diff and digest always be set? (even
fgorski 2014/05/16 20:48:47 Done. Replaced with DVLOG
68 // There are no changes as digest is the same or no settings provided. 221 return false;
69 return false; 222 }
70 } 223
71 224 if (!checkin_response.has_digest()) {
72 std::map<std::string, std::string> settings; 225 LOG(ERROR) << "Field digest not set in response.";
226 return false;
227 }
228
229 if (digest_ == checkin_response.digest())
230 return false;
231
232 bool settings_diff = checkin_response.settings_diff();
233 SettingsMap new_settings;
234 // Only reuse the existing settings, if we are given a settings difference.
235 if (settings_diff)
236 new_settings = settings_map();
237
73 for (int i = 0; i < checkin_response.setting_size(); ++i) { 238 for (int i = 0; i < checkin_response.setting_size(); ++i) {
74 std::string name = checkin_response.setting(i).name(); 239 std::string name = checkin_response.setting(i).name();
75 std::string value = checkin_response.setting(i).value(); 240 if (name.empty())
76 settings[name] = value; 241 return false;
77 } 242
78 243 if (settings_diff && name.find(kDeleteSettingPrefix) == 0) {
79 // Only update the settings in store and digest, if the settings actually 244 std::string setting_to_delete =
80 // passed the verificaiton in update settings. 245 name.substr(arraysize(kDeleteSettingPrefix) - 1);
81 if (UpdateSettings(settings)) { 246 new_settings.erase(setting_to_delete);
82 digest_ = checkin_response.digest(); 247 } else {
83 return true; 248 new_settings[name] = checkin_response.setting(i).value();
84 } 249 }
85 250 }
86 return false; 251
252 if (!VerifySettings(new_settings))
253 return false;
254
255 std::string digest = CalculateDigest(new_settings);
256 if (digest != checkin_response.digest()) {
257 LOG(ERROR) << "G-services settings digest mismatch. "
258 << "Checkin response contains: " << checkin_response.digest()
259 << ". Calculated digest is: " << digest;
260 return false;
261 }
262
263 settings_.swap(new_settings);
264 digest_ = digest;
265 return true;
87 } 266 }
88 267
89 void GServicesSettings::UpdateFromLoadResult( 268 void GServicesSettings::UpdateFromLoadResult(
90 const GCMStore::LoadResult& load_result) { 269 const GCMStore::LoadResult& load_result) {
91 if (UpdateSettings(load_result.gservices_settings)) 270 // No need to try to update settings when load_result is empty.
271 if (load_result.gservices_settings.empty())
272 return;
273 if (VerifySettings(load_result.gservices_settings) &&
274 VerifyDigest(load_result.gservices_settings,
275 load_result.gservices_digest)) {
276 settings_ = load_result.gservices_settings;
92 digest_ = load_result.gservices_digest; 277 digest_ = load_result.gservices_digest;
93 } 278 }
94 279 }
95 std::map<std::string, std::string> GServicesSettings::GetSettingsMap() const { 280
96 std::map<std::string, std::string> settings; 281 base::TimeDelta GServicesSettings::GetCheckinInterval() const {
97 settings[kCheckinIntervalKey] = 282 int64 checkin_interval = kMinimumCheckinInterval;
98 base::Int64ToString(checkin_interval_.InSeconds()); 283 SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
99 settings[kCheckinURLKey] = checkin_url_.spec(); 284 if (iter == settings_.end() ||
100 settings[kMCSHostnameKey] = mcs_main_endpoint_.host(); 285 !base::StringToInt64(iter->second, &checkin_interval)) {
101 settings[kMCSSecurePortKey] = mcs_main_endpoint_.port(); 286 checkin_interval = kDefaultCheckinInterval;
102 settings[kRegistrationURLKey] = registration_url_.spec(); 287 }
103 return settings; 288
104 } 289 if (checkin_interval < kMinimumCheckinInterval)
105 290 checkin_interval = kMinimumCheckinInterval;
106 bool GServicesSettings::UpdateSettings( 291
107 const std::map<std::string, std::string>& settings) { 292 return base::TimeDelta::FromSeconds(checkin_interval);
108 int64 new_checkin_interval = kMinimumCheckinInterval; 293 }
109 std::map<std::string, std::string>::const_iterator iter = 294
110 settings.find(kCheckinIntervalKey); 295 GURL GServicesSettings::GetCheckinURL() const {
111 if (iter == settings.end()) { 296 SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
112 LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey; 297 if (iter == settings_.end() || iter->second.empty())
113 return false; 298 return GURL(kDefaultCheckinURL);
114 } 299 return GURL(iter->second);
115 if (!base::StringToInt64(iter->second, &new_checkin_interval)) { 300 }
116 LOG(ERROR) << "Failed to parse checkin interval: " << iter->second; 301
117 return false; 302 GURL GServicesSettings::GetMCSMainEndpoint() const {
118 } 303 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
119 if (new_checkin_interval < kMinimumCheckinInterval) { 304 if (iter == settings_.end() || iter->second.empty())
120 LOG(ERROR) << "Checkin interval: " << new_checkin_interval 305 return DefaultMainMCSEndpoint();
121 << " is less than allowed minimum: " << kMinimumCheckinInterval; 306
122 new_checkin_interval = kMinimumCheckinInterval; 307 std::string mcs_hostname = iter->second;
123 } 308
124 if (new_checkin_interval == std::numeric_limits<int64>::max()) { 309 iter = settings_.find(kMCSSecurePortKey);
125 LOG(ERROR) << "Checkin interval is too big: " << new_checkin_interval; 310 int mcs_secure_port = kDefaultMCSMainSecurePort;
126 return false; 311 if (iter == settings_.end() || iter->second.empty() ||
127 } 312 !base::StringToInt(iter->second, &mcs_secure_port))
128 313 return DefaultMainMCSEndpoint();
129 std::string new_mcs_hostname; 314
130 iter = settings.find(kMCSHostnameKey); 315 GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
131 if (iter == settings.end()) { 316
132 LOG(ERROR) << "Setting not found: " << kMCSHostnameKey; 317 if (!mcs_endpoint.is_valid())
133 return false; 318 return DefaultMainMCSEndpoint();
134 } 319
135 new_mcs_hostname = iter->second; 320 return mcs_endpoint;
136 if (new_mcs_hostname.empty()) { 321 }
137 LOG(ERROR) << "Empty MCS hostname provided."; 322
138 return false; 323 GURL GServicesSettings::GetMCSFallbackEndpoint() const {
139 } 324 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
140 325 if (iter == settings_.end() || iter->second.empty())
141 int new_mcs_secure_port = -1; 326 return DefaultFallbackMCSEndpoint();
142 iter = settings.find(kMCSSecurePortKey); 327
143 if (iter == settings.end()) { 328 GURL mcs_endpoint(
144 LOG(ERROR) << "Setting not found: " << kMCSSecurePortKey; 329 MakeMCSEndpoint(iter->second, kDefaultMCSFallbackSecurePort));
145 return false; 330
146 } 331 if (!mcs_endpoint.is_valid())
147 if (!base::StringToInt(iter->second, &new_mcs_secure_port)) { 332 return DefaultFallbackMCSEndpoint();
148 LOG(ERROR) << "Failed to parse MCS secure port: " << iter->second; 333
149 return false; 334 return mcs_endpoint;
150 } 335 }
151 if (new_mcs_secure_port < 0 || 65535 < new_mcs_secure_port) { 336
152 LOG(ERROR) << "Incorrect port value: " << new_mcs_secure_port; 337 GURL GServicesSettings::GetRegistrationURL() const {
153 return false; 338 SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
154 } 339 if (iter == settings_.end() || iter->second.empty())
155 340 return GURL(kDefaultRegistrationURL);
156 GURL new_mcs_main_endpoint = 341 return GURL(iter->second);
157 GURL(MakeMCSEndpoint(new_mcs_hostname, new_mcs_secure_port));
158 GURL new_mcs_fallback_endpoint =
159 GURL(MakeMCSEndpoint(new_mcs_hostname, kDefaultMCSFallbackSecurePort));
160 if (!new_mcs_main_endpoint.is_valid() ||
161 !new_mcs_fallback_endpoint.is_valid())
162 return false;
163
164 GURL new_checkin_url;
165 iter = settings.find(kCheckinURLKey);
166 if (iter == settings.end()) {
167 LOG(ERROR) << "Setting not found: " << kCheckinURLKey;
168 return false;
169 }
170 new_checkin_url = GURL(iter->second);
171 if (!new_checkin_url.is_valid()) {
172 LOG(ERROR) << "Invalid checkin URL provided: "
173 << new_checkin_url.possibly_invalid_spec();
174 return false;
175 }
176
177 GURL new_registration_url;
178 iter = settings.find(kRegistrationURLKey);
179 if (iter == settings.end()) {
180 LOG(ERROR) << "Setting not found: " << kRegistrationURLKey;
181 return false;
182 }
183 new_registration_url = GURL(iter->second);
184 if (!new_registration_url.is_valid()) {
185 LOG(ERROR) << "Invalid registration URL provided: "
186 << new_registration_url.possibly_invalid_spec();
187 return false;
188 }
189
190 // We only update the settings once all of them are correct.
191 checkin_interval_ = base::TimeDelta::FromSeconds(new_checkin_interval);
192 mcs_main_endpoint_ = new_mcs_main_endpoint;
193 mcs_fallback_endpoint_ = new_mcs_fallback_endpoint;
194 checkin_url_ = new_checkin_url;
195 registration_url_ = new_registration_url;
196 return true;
197 } 342 }
198 343
199 } // namespace gcm 344 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698