OLD | NEW |
| (Empty) |
1 // Copyright 2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/common/ping.h" | |
17 #include "base/scoped_ptr.h" | |
18 #include "omaha/base/constants.h" | |
19 #include "omaha/base/debug.h" | |
20 #include "omaha/base/logging.h" | |
21 #include "omaha/base/scoped_any.h" | |
22 #include "omaha/base/scoped_impersonation.h" | |
23 #include "omaha/base/string.h" | |
24 #include "omaha/base/utils.h" | |
25 #include "omaha/base/vista_utils.h" | |
26 #include "omaha/common/app_registry_utils.h" | |
27 #include "omaha/common/command_line.h" | |
28 #include "omaha/common/command_line_builder.h" | |
29 #include "omaha/common/config_manager.h" | |
30 #include "omaha/common/goopdate_utils.h" | |
31 #include "omaha/common/update_request.h" | |
32 #include "omaha/common/update_response.h" | |
33 #include "omaha/goopdate/app.h" | |
34 #include "omaha/goopdate/app_bundle.h" | |
35 #include "omaha/goopdate/update_request_utils.h" | |
36 #include "omaha/goopdate/update_response_utils.h" | |
37 | |
38 namespace omaha { | |
39 | |
40 const TCHAR* const Ping::kRegKeyPing = _T("Pings"); | |
41 const time64 Ping::kPingExpiry100ns = 10 * kDaysTo100ns; // 10 days. | |
42 | |
43 Ping::Ping(bool is_machine, | |
44 const CString& session_id, | |
45 const CString& install_source) | |
46 : is_machine_(is_machine), | |
47 ping_request_(xml::UpdateRequest::Create(is_machine, | |
48 session_id, | |
49 install_source, | |
50 CString())) { | |
51 } | |
52 | |
53 Ping::~Ping() { | |
54 } | |
55 | |
56 void Ping::BuildRequest(const App* app, bool is_update_check) { | |
57 update_request_utils::BuildRequest(app, is_update_check, ping_request_.get()); | |
58 } | |
59 | |
60 void Ping::LoadAppDataFromExtraArgs(const CommandLineExtraArgs& extra_args) { | |
61 const CString installation_id = GuidToString(extra_args.installation_id); | |
62 for (size_t i = 0; i != extra_args.apps.size(); ++i) { | |
63 AppData app_data; | |
64 app_data.app_id = GuidToString(extra_args.apps[i].app_guid); | |
65 app_data.language = extra_args.language; | |
66 app_data.brand_code = extra_args.brand_code; | |
67 app_data.client_id = extra_args.client_id; | |
68 app_data.installation_id = installation_id; | |
69 app_data.experiment_labels = extra_args.apps[i].experiment_labels; | |
70 apps_data_.push_back(app_data); | |
71 } | |
72 | |
73 omaha_data_.app_id = kGoogleUpdateAppId; | |
74 omaha_data_.language = extra_args.language; | |
75 omaha_data_.brand_code = extra_args.brand_code; | |
76 omaha_data_.client_id = extra_args.client_id; | |
77 omaha_data_.installation_id = installation_id; | |
78 omaha_data_.experiment_labels = extra_args.experiment_labels; | |
79 } | |
80 | |
81 void Ping::LoadOmahaDataFromRegistry() { | |
82 omaha_data_.app_id = kGoogleUpdateAppId; | |
83 app_registry_utils::GetClientStateData( | |
84 is_machine_, | |
85 kGoogleUpdateAppId, | |
86 NULL, | |
87 NULL, // ap is not used yet. | |
88 &omaha_data_.language, | |
89 &omaha_data_.brand_code, | |
90 &omaha_data_.client_id, | |
91 &omaha_data_.installation_id, | |
92 &omaha_data_.experiment_labels); | |
93 } | |
94 | |
95 void Ping::LoadAppDataFromRegistry(const std::vector<CString>& app_ids) { | |
96 for (size_t i = 0; i != app_ids.size(); ++i) { | |
97 AppData app_data; | |
98 app_data.app_id = app_ids[i]; | |
99 app_registry_utils::GetClientStateData( | |
100 is_machine_, | |
101 app_data.app_id, | |
102 &app_data.pv, | |
103 NULL, // ap is not used yet. | |
104 &app_data.language, | |
105 &app_data.brand_code, | |
106 &app_data.client_id, | |
107 &app_data.installation_id, | |
108 &app_data.experiment_labels); | |
109 apps_data_.push_back(app_data); | |
110 } | |
111 | |
112 LoadOmahaDataFromRegistry(); | |
113 } | |
114 | |
115 HRESULT Ping::Send(bool is_fire_and_forget) { | |
116 CORE_LOG(L3, (_T("[Ping::Send]"))); | |
117 | |
118 ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine_)); | |
119 | |
120 if (ping_request_->IsEmpty()) { | |
121 CORE_LOG(L3, (_T("[Ping::Send did not send empty ping]"))); | |
122 return S_FALSE; | |
123 } | |
124 | |
125 CString request_string; | |
126 HRESULT hr = BuildRequestString(&request_string); | |
127 if (FAILED(hr)) { | |
128 CORE_LOG(LE, (_T("[BuildRequestString failed][0x%08x]"), hr)); | |
129 return hr; | |
130 } | |
131 | |
132 const DWORD wait_timeout_ms = is_fire_and_forget ? 0 : INFINITE; | |
133 hr = SendUsingGoogleUpdate(request_string, wait_timeout_ms); | |
134 if (SUCCEEDED(hr)) { | |
135 return hr; | |
136 } | |
137 | |
138 CORE_LOG(LE, (_T("[Ping::SendUsingGoogleUpdate failed][0x%x]"), hr)); | |
139 | |
140 hr = SendInProcess(request_string); | |
141 if (SUCCEEDED(hr)) { | |
142 return hr; | |
143 } | |
144 | |
145 CORE_LOG(LE, (_T("[Ping::SendInProcess failed][0x%x]"), hr)); | |
146 | |
147 return PersistPing(is_machine_, request_string); | |
148 } | |
149 | |
150 void Ping::BuildOmahaPing(const CString& version, | |
151 const CString& next_version, | |
152 const PingEventPtr& ping_event) { | |
153 xml::request::App app(BuildOmahaApp(version, next_version)); | |
154 app.ping_events.push_back(ping_event); | |
155 ping_request_->AddApp(app); | |
156 } | |
157 | |
158 void Ping::BuildOmahaPing(const CString& version, | |
159 const CString& next_version, | |
160 const PingEventPtr& ping_event1, | |
161 const PingEventPtr& ping_event2) { | |
162 xml::request::App app(BuildOmahaApp(version, next_version)); | |
163 app.ping_events.push_back(ping_event1); | |
164 app.ping_events.push_back(ping_event2); | |
165 ping_request_->AddApp(app); | |
166 } | |
167 | |
168 xml::request::App Ping::BuildOmahaApp(const CString& version, | |
169 const CString& next_version) const { | |
170 xml::request::App app; | |
171 | |
172 app.app_id = omaha_data_.app_id; | |
173 app.lang = omaha_data_.language; | |
174 app.brand_code = omaha_data_.brand_code; | |
175 app.client_id = omaha_data_.client_id; | |
176 app.experiments = omaha_data_.experiment_labels; | |
177 app.iid = omaha_data_.installation_id; | |
178 | |
179 app.version = version; | |
180 app.next_version = next_version; | |
181 | |
182 return app; | |
183 } | |
184 | |
185 void Ping::BuildAppsPing(const PingEventPtr& ping_event) { | |
186 for (size_t i = 0; i != apps_data_.size(); ++i) { | |
187 xml::request::App app; | |
188 | |
189 app.version = apps_data_[i].pv; | |
190 app.app_id = apps_data_[i].app_id; | |
191 app.lang = apps_data_[i].language; | |
192 app.brand_code = apps_data_[i].brand_code; | |
193 app.client_id = apps_data_[i].client_id; | |
194 app.experiments = apps_data_[i].experiment_labels; | |
195 app.iid = apps_data_[i].installation_id; | |
196 | |
197 app.ping_events.push_back(ping_event); | |
198 ping_request_->AddApp(app); | |
199 } | |
200 } | |
201 | |
202 HRESULT Ping::SendUsingGoogleUpdate(const CString& request_string, | |
203 DWORD wait_timeout_ms) const { | |
204 CStringA request_string_utf8(WideToUtf8(request_string)); | |
205 CStringA ping_string_utf8; | |
206 WebSafeBase64Escape(request_string_utf8, &ping_string_utf8); | |
207 | |
208 CommandLineBuilder builder(COMMANDLINE_MODE_PING); | |
209 builder.set_ping_string(Utf8ToWideChar(ping_string_utf8, | |
210 ping_string_utf8.GetLength())); | |
211 CString args = builder.GetCommandLineArgs(); | |
212 | |
213 scoped_process ping_process; | |
214 HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine_, | |
215 args, | |
216 address(ping_process)); | |
217 if (FAILED(hr)) { | |
218 CORE_LOG(LE, (_T("[failed to start ping process][0x%08x]"), hr)); | |
219 return hr; | |
220 } | |
221 | |
222 if (wait_timeout_ms) { | |
223 DWORD result = ::WaitForSingleObject(get(ping_process), wait_timeout_ms); | |
224 DWORD exit_code(0); | |
225 if (result == WAIT_OBJECT_0 && | |
226 ::GetExitCodeProcess(get(ping_process), &exit_code)) { | |
227 ASSERT1(exit_code == 0 || FAILED(exit_code)); | |
228 return (exit_code == 0) ? S_OK : exit_code; | |
229 } else { | |
230 if (result == WAIT_TIMEOUT) { | |
231 CORE_LOG(LW, (_T("[ping process did not finish in time][pid=%u]"), | |
232 ::GetProcessId(get(ping_process)))); | |
233 VERIFY1(::TerminateProcess(get(ping_process), UINT_MAX)); | |
234 } | |
235 return E_FAIL; | |
236 } | |
237 } | |
238 | |
239 return S_OK; | |
240 } | |
241 | |
242 HRESULT Ping::SendInProcess(const CString& request_string) const { | |
243 HRESULT hr = SendString(is_machine_, HeadersVector(), request_string); | |
244 if (FAILED(hr)) { | |
245 CORE_LOG(LE, (_T("[SendString failed][0x%08x]"), hr)); | |
246 return hr; | |
247 } | |
248 | |
249 return S_OK; | |
250 } | |
251 | |
252 HRESULT Ping::BuildRequestString(CString* request_string) const { | |
253 ASSERT1(request_string); | |
254 return ping_request_->Serialize(request_string); | |
255 } | |
256 | |
257 CString Ping::GetPingRegPath(bool is_machine) { | |
258 CString ping_reg_path = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE; | |
259 return AppendRegKeyPath(ping_reg_path, kRegKeyPing); | |
260 } | |
261 | |
262 HRESULT Ping::LoadPersistedPings(bool is_machine, PingsVector* pings) { | |
263 ASSERT1(pings); | |
264 | |
265 RegKey ping_reg_key; | |
266 HRESULT hr = ping_reg_key.Open(GetPingRegPath(is_machine), KEY_READ); | |
267 if (FAILED(hr)) { | |
268 CORE_LOG(LW, (_T("[Unable to open Ping regkey][0x%x]"), hr)); | |
269 return hr; | |
270 } | |
271 | |
272 int num_pings = ping_reg_key.GetValueCount(); | |
273 for (int i = 0; i < num_pings; ++i) { | |
274 CString persisted_time_string; | |
275 hr = ping_reg_key.GetValueNameAt(i, &persisted_time_string, NULL); | |
276 if (FAILED(hr)) { | |
277 CORE_LOG(LW, (_T("[GetValueNameAt failed][%d]"), i)); | |
278 continue; | |
279 } | |
280 | |
281 time64 persisted_time = _tcstoui64(persisted_time_string, NULL, 10); | |
282 if (persisted_time == 0 || persisted_time == _UI64_MAX) { | |
283 CORE_LOG(LW, (_T("[Incorrect time value][%s]"), persisted_time_string)); | |
284 continue; | |
285 } | |
286 | |
287 CString ping_string; | |
288 hr = ping_reg_key.GetValue(persisted_time_string, &ping_string); | |
289 if (FAILED(hr)) { | |
290 CORE_LOG(LW, (_T("[GetValue failed][%s]"), persisted_time_string)); | |
291 continue; | |
292 } | |
293 | |
294 pings->push_back(std::make_pair(persisted_time, ping_string)); | |
295 } | |
296 | |
297 return S_OK; | |
298 } | |
299 | |
300 bool Ping::IsPingExpired(time64 persisted_time) { | |
301 const time64 now = GetCurrent100NSTime(); | |
302 | |
303 if (now < persisted_time) { | |
304 CORE_LOG(LW, (_T("[Incorrect clock time][%I64u][%I64u]"), | |
305 now, persisted_time)); | |
306 return true; | |
307 } | |
308 | |
309 const time64 time_difference = now - persisted_time; | |
310 CORE_LOG(L3, (_T("[%I64u][%I64u][%I64u]"), | |
311 now, persisted_time, time_difference)); | |
312 | |
313 const bool result = time_difference >= kPingExpiry100ns; | |
314 CORE_LOG(L3, (_T("[IsPingExpired][%d]"), result)); | |
315 return result; | |
316 } | |
317 | |
318 HRESULT Ping::DeletePersistedPing(bool is_machine, time64 persisted_time) { | |
319 CString persisted_time_string; | |
320 persisted_time_string.Format(_T("%I64u"), persisted_time); | |
321 CORE_LOG(L3, (_T("[Ping::DeletePersistedPing][%s]"), persisted_time_string)); | |
322 | |
323 CString ping_reg_path(GetPingRegPath(is_machine)); | |
324 HRESULT hr = RegKey::DeleteValue(ping_reg_path, persisted_time_string); | |
325 | |
326 if (RegKey::IsKeyEmpty(ping_reg_path)) { | |
327 VERIFY1(SUCCEEDED(RegKey::DeleteKey(ping_reg_path))); | |
328 } | |
329 | |
330 return hr; | |
331 } | |
332 | |
333 HRESULT Ping::PersistPing(bool is_machine, const CString& ping_string) { | |
334 CString time_now_str; | |
335 time_now_str.Format(_T("%I64u"), GetCurrent100NSTime()); | |
336 CORE_LOG(L3, (_T("[Ping::PersistPing][%s][%s]"), time_now_str, ping_string)); | |
337 | |
338 return RegKey::SetValue(GetPingRegPath(is_machine), | |
339 time_now_str, | |
340 ping_string); | |
341 } | |
342 | |
343 HRESULT Ping::SendPersistedPings(bool is_machine) { | |
344 PingsVector pings; | |
345 HRESULT hr = LoadPersistedPings(is_machine, &pings); | |
346 if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))) { | |
347 return hr; | |
348 } | |
349 | |
350 for (size_t i = 0; i != pings.size(); ++i) { | |
351 time64 persisted_time = pings[i].first; | |
352 int32 request_age = Time64ToInt32(GetCurrent100NSTime()) - | |
353 Time64ToInt32(persisted_time); | |
354 const CString& ping_string(pings[i].second); | |
355 | |
356 CORE_LOG(L3, (_T("[Resending ping][%I64u][%d][%s]"), | |
357 persisted_time, request_age, ping_string)); | |
358 | |
359 CString request_age_string; | |
360 request_age_string.Format(_T("%d"), request_age); | |
361 HeadersVector headers; | |
362 headers.push_back(std::make_pair(kHeaderXRequestAge, request_age_string)); | |
363 | |
364 hr = SendString(is_machine, headers, ping_string); | |
365 | |
366 if (SUCCEEDED(hr) || IsPingExpired(persisted_time)) { | |
367 CORE_LOG(L3, (_T("[Deleting ping][0x%x]"), hr)); | |
368 VERIFY1(SUCCEEDED(DeletePersistedPing(is_machine, persisted_time))); | |
369 } | |
370 } | |
371 | |
372 return S_OK; | |
373 } | |
374 | |
375 // TODO(omaha): Ping support for authenticated proxies. | |
376 HRESULT Ping::SendString(bool is_machine, | |
377 const HeadersVector& headers, | |
378 const CString& request_string) { | |
379 ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine)); | |
380 | |
381 CORE_LOG(L3, (_T("[ping request string][%s]"), request_string)); | |
382 | |
383 CString url; | |
384 ConfigManager::Instance()->GetPingUrl(&url); | |
385 | |
386 // Impersonate the user if the caller is machine, running as local system, | |
387 // and a user is logged on to the system. | |
388 scoped_handle impersonation_token( | |
389 goopdate_utils::GetImpersonationTokenForMachineProcess(is_machine)); | |
390 scoped_impersonation impersonate_user(get(impersonation_token)); | |
391 | |
392 WebServicesClient web_service_client(is_machine); | |
393 HRESULT hr(web_service_client.Initialize(url, headers, false)); | |
394 if (FAILED(hr)) { | |
395 CORE_LOG(LE, (_T("[WebServicesClient::Initialize failed][0x%08x]"), hr)); | |
396 return hr; | |
397 } | |
398 | |
399 scoped_ptr<xml::UpdateResponse> response(xml::UpdateResponse::Create()); | |
400 hr = web_service_client.SendString(&request_string, response.get()); | |
401 if (FAILED(hr)) { | |
402 CORE_LOG(LE, (_T("[WebServicesClient::SendString failed][0x%08x]"), hr)); | |
403 return hr; | |
404 } | |
405 | |
406 // If a ping is sent but the response is corrupted in some way (or we can't | |
407 // persist the labels for some reason), returning a failure code would result | |
408 // in the ping being persisted and re-sent later. For this reason, we always | |
409 // return a success code if we sent the ping, even if following actions fail. | |
410 VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas( | |
411 is_machine, | |
412 response.get()))); | |
413 | |
414 return S_OK; | |
415 } | |
416 | |
417 HRESULT Ping::HandlePing(bool is_machine, const CString& ping_string) { | |
418 CORE_LOG(L3, (_T("[Ping::HandlePing][%s]"), ping_string)); | |
419 | |
420 CStringA ping_string_utf8(WideToUtf8(ping_string)); | |
421 | |
422 CStringA request_string_utf8; | |
423 int out_buffer_length = ping_string_utf8.GetLength(); | |
424 char* out_buffer = request_string_utf8.GetBufferSetLength(out_buffer_length); | |
425 | |
426 out_buffer_length = WebSafeBase64Unescape(ping_string_utf8, | |
427 ping_string_utf8.GetLength(), | |
428 out_buffer, | |
429 out_buffer_length); | |
430 ASSERT1(out_buffer_length <= ping_string_utf8.GetLength()); | |
431 request_string_utf8.ReleaseBufferSetLength(out_buffer_length); | |
432 | |
433 CString request_string(Utf8ToWideChar(request_string_utf8, | |
434 request_string_utf8.GetLength())); | |
435 | |
436 return Ping::SendString(is_machine, HeadersVector(), request_string); | |
437 } | |
438 | |
439 } // namespace omaha | |
440 | |
OLD | NEW |