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

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

Powered by Google App Engine
This is Rietveld 408576698