1 //
2 // Copyright (C) 2009 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/libcurl_http_fetcher.h"
18
19 #include <netinet/in.h>
20 #include <resolv.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <algorithm>
25 #include <string>
26
27 #include <base/bind.h>
28 #include <base/format_macros.h>
29 #include <base/location.h>
30 #include <base/logging.h>
31 #include <base/strings/string_split.h>
32 #include <base/strings/string_util.h>
33 #include <base/strings/stringprintf.h>
34 #include <base/threading/thread_task_runner_handle.h>
35
36 #ifdef __ANDROID__
37 #include <cutils/qtaguid.h>
38 #include <private/android_filesystem_config.h>
39 #endif // __ANDROID__
40
41 #include "update_engine/certificate_checker.h"
42 #include "update_engine/common/hardware_interface.h"
43 #include "update_engine/common/platform_constants.h"
44 #include "update_engine/common/utils.h"
45
46 using base::TimeDelta;
47 using brillo::MessageLoop;
48 using std::max;
49 using std::string;
50
51 // This is a concrete implementation of HttpFetcher that uses libcurl to do the
52 // http work.
53
54 namespace chromeos_update_engine {
55
56 namespace {
57
58 const int kNoNetworkRetrySeconds = 10;
59
60 // libcurl's CURLOPT_SOCKOPTFUNCTION callback function. Called after the socket
61 // is created but before it is connected. This callback tags the created socket
62 // so the network usage can be tracked in Android.
LibcurlSockoptCallback(void *,curl_socket_t curlfd,curlsocktype)63 int LibcurlSockoptCallback(void* /* clientp */,
64 curl_socket_t curlfd,
65 curlsocktype /* purpose */) {
66 #ifdef __ANDROID__
67 // Socket tag used by all network sockets. See qtaguid kernel module for
68 // stats.
69 const int kUpdateEngineSocketTag = 0x55417243; // "CrAU" in little-endian.
70 qtaguid_tagSocket(curlfd, kUpdateEngineSocketTag, AID_OTA_UPDATE);
71 #endif // __ANDROID__
72 return CURL_SOCKOPT_OK;
73 }
74
75 } // namespace
76
77 // static
LibcurlCloseSocketCallback(void * clientp,curl_socket_t item)78 int LibcurlHttpFetcher::LibcurlCloseSocketCallback(void* clientp,
79 curl_socket_t item) {
80 #ifdef __ANDROID__
81 qtaguid_untagSocket(item);
82 #endif // __ANDROID__
83
84 LibcurlHttpFetcher* fetcher = static_cast<LibcurlHttpFetcher*>(clientp);
85 // Stop watching the socket before closing it.
86 for (size_t t = 0; t < base::size(fetcher->fd_controller_maps_); ++t) {
87 fetcher->fd_controller_maps_[t].erase(item);
88 }
89
90 // Documentation for this callback says to return 0 on success or 1 on error.
91 if (!IGNORE_EINTR(close(item)))
92 return 0;
93 return 1;
94 }
95
LibcurlHttpFetcher(HardwareInterface * hardware)96 LibcurlHttpFetcher::LibcurlHttpFetcher(HardwareInterface* hardware)
97 : hardware_(hardware) {
98 // Dev users want a longer timeout (180 seconds) because they may
99 // be waiting on the dev server to build an image.
100 if (!hardware_->IsOfficialBuild())
101 low_speed_time_seconds_ = kDownloadDevModeLowSpeedTimeSeconds;
102 if (hardware_->IsOOBEEnabled() && !hardware_->IsOOBEComplete(nullptr))
103 max_retry_count_ = kDownloadMaxRetryCountOobeNotComplete;
104 }
105
~LibcurlHttpFetcher()106 LibcurlHttpFetcher::~LibcurlHttpFetcher() {
107 LOG_IF(ERROR, transfer_in_progress_)
108 << "Destroying the fetcher while a transfer is in progress.";
109 CleanUp();
110 }
111
GetProxyType(const string & proxy,curl_proxytype * out_type)112 bool LibcurlHttpFetcher::GetProxyType(const string& proxy,
113 curl_proxytype* out_type) {
114 if (base::StartsWith(
115 proxy, "socks5://", base::CompareCase::INSENSITIVE_ASCII) ||
116 base::StartsWith(
117 proxy, "socks://", base::CompareCase::INSENSITIVE_ASCII)) {
118 *out_type = CURLPROXY_SOCKS5_HOSTNAME;
119 return true;
120 }
121 if (base::StartsWith(
122 proxy, "socks4://", base::CompareCase::INSENSITIVE_ASCII)) {
123 *out_type = CURLPROXY_SOCKS4A;
124 return true;
125 }
126 if (base::StartsWith(
127 proxy, "http://", base::CompareCase::INSENSITIVE_ASCII) ||
128 base::StartsWith(
129 proxy, "https://", base::CompareCase::INSENSITIVE_ASCII)) {
130 *out_type = CURLPROXY_HTTP;
131 return true;
132 }
133 if (base::StartsWith(proxy, kNoProxy, base::CompareCase::INSENSITIVE_ASCII)) {
134 // known failure case. don't log.
135 return false;
136 }
137 LOG(INFO) << "Unknown proxy type: " << proxy;
138 return false;
139 }
140
ResumeTransfer(const string & url)141 void LibcurlHttpFetcher::ResumeTransfer(const string& url) {
142 LOG(INFO) << "Starting/Resuming transfer";
143 CHECK(!transfer_in_progress_);
144 url_ = url;
145 curl_multi_handle_ = curl_multi_init();
146 CHECK(curl_multi_handle_);
147
148 curl_handle_ = curl_easy_init();
149 CHECK(curl_handle_);
150 ignore_failure_ = false;
151
152 // Tag and untag the socket for network usage stats.
153 curl_easy_setopt(
154 curl_handle_, CURLOPT_SOCKOPTFUNCTION, LibcurlSockoptCallback);
155 curl_easy_setopt(
156 curl_handle_, CURLOPT_CLOSESOCKETFUNCTION, LibcurlCloseSocketCallback);
157 curl_easy_setopt(curl_handle_, CURLOPT_CLOSESOCKETDATA, this);
158
159 CHECK(HasProxy());
160 bool is_direct = (GetCurrentProxy() == kNoProxy);
161 LOG(INFO) << "Using proxy: " << (is_direct ? "no" : "yes");
162 if (is_direct) {
163 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROXY, ""), CURLE_OK);
164 } else {
165 CHECK_EQ(curl_easy_setopt(
166 curl_handle_, CURLOPT_PROXY, GetCurrentProxy().c_str()),
167 CURLE_OK);
168 // Curl seems to require us to set the protocol
169 curl_proxytype type{};
170 if (GetProxyType(GetCurrentProxy(), &type)) {
171 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROXYTYPE, type),
172 CURLE_OK);
173 }
174 }
175
176 if (post_data_set_) {
177 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POST, 1), CURLE_OK);
178 CHECK_EQ(
179 curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS, post_data_.data()),
180 CURLE_OK);
181 CHECK_EQ(curl_easy_setopt(
182 curl_handle_, CURLOPT_POSTFIELDSIZE, post_data_.size()),
183 CURLE_OK);
184 }
185
186 // Setup extra HTTP headers.
187 if (curl_http_headers_) {
188 curl_slist_free_all(curl_http_headers_);
189 curl_http_headers_ = nullptr;
190 }
191 for (const auto& header : extra_headers_) {
192 // curl_slist_append() copies the string.
193 curl_http_headers_ =
194 curl_slist_append(curl_http_headers_, header.second.c_str());
195 }
196 if (post_data_set_) {
197 // Set the Content-Type HTTP header, if one was specifically set.
198 if (post_content_type_ != kHttpContentTypeUnspecified) {
199 const string content_type_attr = base::StringPrintf(
200 "Content-Type: %s", GetHttpContentTypeString(post_content_type_));
201 curl_http_headers_ =
202 curl_slist_append(curl_http_headers_, content_type_attr.c_str());
203 } else {
204 LOG(WARNING) << "no content type set, using libcurl default";
205 }
206 }
207 CHECK_EQ(
208 curl_easy_setopt(curl_handle_, CURLOPT_HTTPHEADER, curl_http_headers_),
209 CURLE_OK);
210
211 if (bytes_downloaded_ > 0 || download_length_) {
212 // Resume from where we left off.
213 resume_offset_ = bytes_downloaded_;
214 CHECK_GE(resume_offset_, 0);
215
216 // Compute end offset, if one is specified. As per HTTP specification, this
217 // is an inclusive boundary. Make sure it doesn't overflow.
218 size_t end_offset = 0;
219 if (download_length_) {
220 end_offset = static_cast<size_t>(resume_offset_) + download_length_ - 1;
221 CHECK_LE((size_t)resume_offset_, end_offset);
222 }
223
224 // Create a string representation of the desired range.
225 string range_str = base::StringPrintf(
226 "%" PRIu64 "-", static_cast<uint64_t>(resume_offset_));
227 if (end_offset)
228 range_str += std::to_string(end_offset);
229 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_RANGE, range_str.c_str()),
230 CURLE_OK);
231 }
232
233 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK);
234 CHECK_EQ(
235 curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION, StaticLibcurlWrite),
236 CURLE_OK);
237 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()), CURLE_OK);
238
239 // If the connection drops under |low_speed_limit_bps_| (10
240 // bytes/sec by default) for |low_speed_time_seconds_| (90 seconds,
241 // 180 on non-official builds), reconnect.
242 CHECK_EQ(curl_easy_setopt(
243 curl_handle_, CURLOPT_LOW_SPEED_LIMIT, low_speed_limit_bps_),
244 CURLE_OK);
245 CHECK_EQ(curl_easy_setopt(
246 curl_handle_, CURLOPT_LOW_SPEED_TIME, low_speed_time_seconds_),
247 CURLE_OK);
248 CHECK_EQ(curl_easy_setopt(
249 curl_handle_, CURLOPT_CONNECTTIMEOUT, connect_timeout_seconds_),
250 CURLE_OK);
251
252 // By default, libcurl doesn't follow redirections. Allow up to
253 // |kDownloadMaxRedirects| redirections.
254 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_FOLLOWLOCATION, 1), CURLE_OK);
255 CHECK_EQ(
256 curl_easy_setopt(curl_handle_, CURLOPT_MAXREDIRS, kDownloadMaxRedirects),
257 CURLE_OK);
258
259 // Lock down the appropriate curl options for HTTP or HTTPS depending on
260 // the url.
261 if (hardware_->IsOfficialBuild()) {
262 if (base::StartsWith(
263 url_, "http://", base::CompareCase::INSENSITIVE_ASCII)) {
264 SetCurlOptionsForHttp();
265 } else if (base::StartsWith(
266 url_, "https://", base::CompareCase::INSENSITIVE_ASCII)) {
267 SetCurlOptionsForHttps();
268 #ifdef __ANDROID__
269 } else if (base::StartsWith(
270 url_, "file://", base::CompareCase::INSENSITIVE_ASCII)) {
271 SetCurlOptionsForFile();
272 #endif // __ANDROID__
273 } else {
274 LOG(ERROR) << "Received invalid URI: " << url_;
275 // Lock down to no protocol supported for the transfer.
276 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, 0), CURLE_OK);
277 }
278 } else {
279 LOG(INFO) << "Not setting http(s) curl options because we are "
280 << "running a dev/test image";
281 }
282
283 CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK);
284 transfer_in_progress_ = true;
285 }
286
287 // Lock down only the protocol in case of HTTP.
SetCurlOptionsForHttp()288 void LibcurlHttpFetcher::SetCurlOptionsForHttp() {
289 LOG(INFO) << "Setting up curl options for HTTP";
290 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_HTTP),
291 CURLE_OK);
292 CHECK_EQ(
293 curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP),
294 CURLE_OK);
295 }
296
297 // Security lock-down in official builds: makes sure that peer certificate
298 // verification is enabled, restricts the set of trusted certificates,
299 // restricts protocols to HTTPS, restricts ciphers to HIGH.
SetCurlOptionsForHttps()300 void LibcurlHttpFetcher::SetCurlOptionsForHttps() {
301 LOG(INFO) << "Setting up curl options for HTTPS";
302 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYPEER, 1), CURLE_OK);
303 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYHOST, 2), CURLE_OK);
304 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_CAINFO, nullptr), CURLE_OK);
305 CHECK_EQ(curl_easy_setopt(
306 curl_handle_, CURLOPT_CAPATH, constants::kCACertificatesPath),
307 CURLE_OK);
308 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS),
309 CURLE_OK);
310 CHECK_EQ(
311 curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS),
312 CURLE_OK);
313 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_CIPHER_LIST, "HIGH:!ADH"),
314 CURLE_OK);
315 if (server_to_check_ != ServerToCheck::kNone) {
316 CHECK_EQ(
317 curl_easy_setopt(curl_handle_, CURLOPT_SSL_CTX_DATA, &server_to_check_),
318 CURLE_OK);
319 CHECK_EQ(curl_easy_setopt(curl_handle_,
320 CURLOPT_SSL_CTX_FUNCTION,
321 CertificateChecker::ProcessSSLContext),
322 CURLE_OK);
323 }
324 }
325
326 // Lock down only the protocol in case of a local file.
SetCurlOptionsForFile()327 void LibcurlHttpFetcher::SetCurlOptionsForFile() {
328 LOG(INFO) << "Setting up curl options for FILE";
329 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_FILE),
330 CURLE_OK);
331 CHECK_EQ(
332 curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_FILE),
333 CURLE_OK);
334 }
335
336 // Begins the transfer, which must not have already been started.
BeginTransfer(const string & url)337 void LibcurlHttpFetcher::BeginTransfer(const string& url) {
338 CHECK(!transfer_in_progress_);
339 url_ = url;
340
341 transfer_size_ = -1;
342 resume_offset_ = 0;
343 retry_count_ = 0;
344 no_network_retry_count_ = 0;
345 http_response_code_ = 0;
346 terminate_requested_ = false;
347 sent_byte_ = false;
348
349 // If we are paused, we delay these two operations until Unpause is called.
350 if (transfer_paused_) {
351 restart_transfer_on_unpause_ = true;
352 return;
353 }
354 ResumeTransfer(url_);
355 CurlPerformOnce();
356 }
357
ForceTransferTermination()358 void LibcurlHttpFetcher::ForceTransferTermination() {
359 CleanUp();
360 if (delegate_) {
361 // Note that after the callback returns this object may be destroyed.
362 delegate_->TransferTerminated(this);
363 }
364 }
365
TerminateTransfer()366 void LibcurlHttpFetcher::TerminateTransfer() {
367 if (in_write_callback_) {
368 terminate_requested_ = true;
369 } else {
370 ForceTransferTermination();
371 }
372 }
373
SetHeader(const string & header_name,const string & header_value)374 void LibcurlHttpFetcher::SetHeader(const string& header_name,
375 const string& header_value) {
376 string header_line = header_name + ": " + header_value;
377 // Avoid the space if no data on the right side of the semicolon.
378 if (header_value.empty())
379 header_line = header_name + ":";
380 TEST_AND_RETURN(header_line.find('\n') == string::npos);
381 TEST_AND_RETURN(header_name.find(':') == string::npos);
382 extra_headers_[base::ToLowerASCII(header_name)] = header_line;
383 }
384
385 // Inputs: header_name, header_value
386 // Example:
387 // extra_headers_ = { {"foo":"foo: 123"}, {"bar":"bar:"} }
388 // string tmp = "gibberish";
389 // Case 1:
390 // GetHeader("foo", &tmp) -> tmp = "123", return true.
391 // Case 2:
392 // GetHeader("bar", &tmp) -> tmp = "", return true.
393 // Case 3:
394 // GetHeader("moo", &tmp) -> tmp = "", return false.
GetHeader(const string & header_name,string * header_value) const395 bool LibcurlHttpFetcher::GetHeader(const string& header_name,
396 string* header_value) const {
397 // Initially clear |header_value| to handle both success and failures without
398 // leaving |header_value| in a unclear state.
399 header_value->clear();
400 auto header_key = base::ToLowerASCII(header_name);
401 auto header_line_itr = extra_headers_.find(header_key);
402 // If the |header_name| was never set, indicate so by returning false.
403 if (header_line_itr == extra_headers_.end())
404 return false;
405 // From |SetHeader()| the check for |header_name| to not include ":" is
406 // verified, so finding the first index of ":" is a safe operation.
407 auto header_line = header_line_itr->second;
408 *header_value = header_line.substr(header_line.find(':') + 1);
409 // The following is neccessary to remove the leading ' ' before the header
410 // value that was place only if |header_value| passed to |SetHeader()| was
411 // a non-empty string.
412 header_value->erase(0, 1);
413 return true;
414 }
415
CurlPerformOnce()416 void LibcurlHttpFetcher::CurlPerformOnce() {
417 CHECK(transfer_in_progress_);
418 int running_handles = 0;
419 CURLMcode retcode = CURLM_CALL_MULTI_PERFORM;
420
421 // libcurl may request that we immediately call curl_multi_perform after it
422 // returns, so we do. libcurl promises that curl_multi_perform will not block.
423 while (CURLM_CALL_MULTI_PERFORM == retcode) {
424 retcode = curl_multi_perform(curl_multi_handle_, &running_handles);
425 if (terminate_requested_) {
426 ForceTransferTermination();
427 return;
428 }
429 }
430
431 // When retcode is not |CURLM_OK| at this point, libcurl has an internal error
432 // that it is less likely to recover from (libcurl bug, out-of-memory, etc.).
433 // In case of an update check, we send UMA metrics and log the error.
434 if (is_update_check_ &&
435 (retcode == CURLM_OUT_OF_MEMORY || retcode == CURLM_INTERNAL_ERROR)) {
436 auxiliary_error_code_ = ErrorCode::kInternalLibCurlError;
437 LOG(ERROR) << "curl_multi_perform is in an unrecoverable error condition: "
438 << retcode;
439 } else if (retcode != CURLM_OK) {
440 LOG(ERROR) << "curl_multi_perform returns error: " << retcode;
441 }
442
443 // If the transfer completes while paused, we should ignore the failure once
444 // the fetcher is unpaused.
445 if (running_handles == 0 && transfer_paused_ && !ignore_failure_) {
446 LOG(INFO) << "Connection closed while paused, ignoring failure.";
447 ignore_failure_ = true;
448 }
449
450 if (running_handles != 0 || transfer_paused_) {
451 // There's either more work to do or we are paused, so we just keep the
452 // file descriptors to watch up to date and exit, until we are done with the
453 // work and we are not paused.
454 //
455 // When there's no |base::SingleThreadTaskRunner| on current thread, it's
456 // not possible to watch file descriptors. Just poll it later. This usually
457 // happens if |brillo::FakeMessageLoop| is used.
458 if (!base::ThreadTaskRunnerHandle::IsSet()) {
459 MessageLoop::current()->PostDelayedTask(
460 FROM_HERE,
461 base::Bind(&LibcurlHttpFetcher::CurlPerformOnce,
462 base::Unretained(this)),
463 TimeDelta::FromSeconds(1));
464 return;
465 }
466 SetupMessageLoopSources();
467 return;
468 }
469
470 // At this point, the transfer was completed in some way (error, connection
471 // closed or download finished).
472
473 GetHttpResponseCode();
474 if (http_response_code_) {
475 LOG(INFO) << "HTTP response code: " << http_response_code_;
476 no_network_retry_count_ = 0;
477 unresolved_host_state_machine_.UpdateState(false);
478 } else {
479 LOG(ERROR) << "Unable to get http response code.";
480 CURLcode curl_code = GetCurlCode();
481 LOG(ERROR) << "Return code for the transfer: " << curl_code;
482 if (curl_code == CURLE_COULDNT_RESOLVE_HOST) {
483 LOG(ERROR) << "libcurl can not resolve host.";
484 unresolved_host_state_machine_.UpdateState(true);
485 auxiliary_error_code_ = ErrorCode::kUnresolvedHostError;
486 }
487 }
488
489 // we're done!
490 CleanUp();
491
492 if (unresolved_host_state_machine_.GetState() ==
493 UnresolvedHostStateMachine::State::kRetry) {
494 // Based on
495 // https://curl.haxx.se/docs/todo.html#updated_DNS_server_while_running,
496 // update_engine process should call res_init() and unconditionally retry.
497 res_init();
498 no_network_max_retries_++;
499 LOG(INFO) << "Will retry after reloading resolv.conf because last attempt "
500 "failed to resolve host.";
501 } else if (unresolved_host_state_machine_.GetState() ==
502 UnresolvedHostStateMachine::State::kRetriedSuccess) {
503 auxiliary_error_code_ = ErrorCode::kUnresolvedHostRecovered;
504 }
505
506 // TODO(petkov): This temporary code tries to deal with the case where the
507 // update engine performs an update check while the network is not ready
508 // (e.g., right after resume). Longer term, we should check if the network
509 // is online/offline and return an appropriate error code.
510 if (!sent_byte_ && http_response_code_ == 0 &&
511 no_network_retry_count_ < no_network_max_retries_) {
512 no_network_retry_count_++;
513 retry_task_id_ = MessageLoop::current()->PostDelayedTask(
514 FROM_HERE,
515 base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
516 base::Unretained(this)),
517 TimeDelta::FromSeconds(kNoNetworkRetrySeconds));
518 LOG(INFO) << "No HTTP response, retry " << no_network_retry_count_;
519 } else if ((!sent_byte_ && !IsHttpResponseSuccess()) ||
520 IsHttpResponseError()) {
521 // The transfer completed w/ error and we didn't get any bytes.
522 // If we have another proxy to try, try that.
523 //
524 // TODO(garnold) in fact there are two separate cases here: one case is an
525 // other-than-success return code (including no return code) and no
526 // received bytes, which is necessary due to the way callbacks are
527 // currently processing error conditions; the second is an explicit HTTP
528 // error code, where some data may have been received (as in the case of a
529 // semi-successful multi-chunk fetch). This is a confusing behavior and
530 // should be unified into a complete, coherent interface.
531 LOG(INFO) << "Transfer resulted in an error (" << http_response_code_
532 << "), " << bytes_downloaded_ << " bytes downloaded";
533
534 PopProxy(); // Delete the proxy we just gave up on.
535
536 if (HasProxy()) {
537 // We have another proxy. Retry immediately.
538 LOG(INFO) << "Retrying with next proxy setting";
539 retry_task_id_ = MessageLoop::current()->PostTask(
540 FROM_HERE,
541 base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
542 base::Unretained(this)));
543 } else {
544 // Out of proxies. Give up.
545 LOG(INFO) << "No further proxies, indicating transfer complete";
546 if (delegate_)
547 delegate_->TransferComplete(this, false); // signal fail
548 return;
549 }
550 } else if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) {
551 if (!ignore_failure_)
552 retry_count_++;
553 LOG(INFO) << "Transfer interrupted after downloading " << bytes_downloaded_
554 << " of " << transfer_size_ << " bytes. "
555 << transfer_size_ - bytes_downloaded_ << " bytes remaining "
556 << "after " << retry_count_ << " attempt(s)";
557
558 if (retry_count_ > max_retry_count_) {
559 LOG(INFO) << "Reached max attempts (" << retry_count_ << ")";
560 if (delegate_)
561 delegate_->TransferComplete(this, false); // signal fail
562 return;
563 }
564 // Need to restart transfer
565 LOG(INFO) << "Restarting transfer to download the remaining bytes";
566 retry_task_id_ = MessageLoop::current()->PostDelayedTask(
567 FROM_HERE,
568 base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
569 base::Unretained(this)),
570 TimeDelta::FromSeconds(retry_seconds_));
571 } else {
572 LOG(INFO) << "Transfer completed (" << http_response_code_ << "), "
573 << bytes_downloaded_ << " bytes downloaded";
574 if (delegate_) {
575 bool success = IsHttpResponseSuccess();
576 delegate_->TransferComplete(this, success);
577 }
578 return;
579 }
580 // If we reach this point is because TransferComplete() was not called in any
581 // of the previous branches. The delegate is allowed to destroy the object
582 // once TransferComplete is called so this would be illegal.
583 ignore_failure_ = false;
584 }
585
LibcurlWrite(void * ptr,size_t size,size_t nmemb)586 size_t LibcurlHttpFetcher::LibcurlWrite(void* ptr, size_t size, size_t nmemb) {
587 // Update HTTP response first.
588 GetHttpResponseCode();
589 const size_t payload_size = size * nmemb;
590
591 // Do nothing if no payload or HTTP response is an error.
592 if (payload_size == 0 || !IsHttpResponseSuccess()) {
593 LOG(INFO) << "HTTP response unsuccessful (" << http_response_code_
594 << ") or no payload (" << payload_size << "), nothing to do";
595 return 0;
596 }
597
598 sent_byte_ = true;
599 {
600 double transfer_size_double{};
601 CHECK_EQ(curl_easy_getinfo(curl_handle_,
602 CURLINFO_CONTENT_LENGTH_DOWNLOAD,
603 &transfer_size_double),
604 CURLE_OK);
605 off_t new_transfer_size = static_cast<off_t>(transfer_size_double);
606 if (new_transfer_size > 0) {
607 transfer_size_ = resume_offset_ + new_transfer_size;
608 }
609 }
610 bytes_downloaded_ += payload_size;
611 if (delegate_) {
612 in_write_callback_ = true;
613 auto should_terminate = !delegate_->ReceivedBytes(this, ptr, payload_size);
614 in_write_callback_ = false;
615 if (should_terminate) {
616 LOG(INFO) << "Requesting libcurl to terminate transfer.";
617 // Returning an amount that differs from the received size signals an
618 // error condition to libcurl, which will cause the transfer to be
619 // aborted.
620 return 0;
621 }
622 }
623 return payload_size;
624 }
625
Pause()626 void LibcurlHttpFetcher::Pause() {
627 if (transfer_paused_) {
628 LOG(ERROR) << "Fetcher already paused.";
629 return;
630 }
631 transfer_paused_ = true;
632 if (!transfer_in_progress_) {
633 // If pause before we started a connection, we don't need to notify curl
634 // about that, we will simply not start the connection later.
635 return;
636 }
637 CHECK(curl_handle_);
638 CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_ALL), CURLE_OK);
639 }
640
Unpause()641 void LibcurlHttpFetcher::Unpause() {
642 if (!transfer_paused_) {
643 LOG(ERROR) << "Resume attempted when fetcher not paused.";
644 return;
645 }
646 transfer_paused_ = false;
647 if (restart_transfer_on_unpause_) {
648 restart_transfer_on_unpause_ = false;
649 ResumeTransfer(url_);
650 CurlPerformOnce();
651 return;
652 }
653 if (!transfer_in_progress_) {
654 // If resumed before starting the connection, there's no need to notify
655 // anybody. We will simply start the connection once it is time.
656 return;
657 }
658 CHECK(curl_handle_);
659 CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_CONT), CURLE_OK);
660 // Since the transfer is in progress, we need to dispatch a CurlPerformOnce()
661 // now to let the connection continue, otherwise it would be called by the
662 // TimeoutCallback but with a delay.
663 CurlPerformOnce();
664 }
665
666 // This method sets up callbacks with the MessageLoop.
SetupMessageLoopSources()667 void LibcurlHttpFetcher::SetupMessageLoopSources() {
668 fd_set fd_read;
669 fd_set fd_write;
670 fd_set fd_exc;
671
672 FD_ZERO(&fd_read);
673 FD_ZERO(&fd_write);
674 FD_ZERO(&fd_exc);
675
676 int fd_max = 0;
677
678 // Ask libcurl for the set of file descriptors we should track on its
679 // behalf.
680 CHECK_EQ(curl_multi_fdset(
681 curl_multi_handle_, &fd_read, &fd_write, &fd_exc, &fd_max),
682 CURLM_OK);
683
684 // We should iterate through all file descriptors up to libcurl's fd_max or
685 // the highest one we're tracking, whichever is larger.
686 for (size_t t = 0; t < base::size(fd_controller_maps_); ++t) {
687 if (!fd_controller_maps_[t].empty())
688 fd_max = max(fd_max, fd_controller_maps_[t].rbegin()->first);
689 }
690
691 // For each fd, if we're not tracking it, track it. If we are tracking it, but
692 // libcurl doesn't care about it anymore, stop tracking it. After this loop,
693 // there should be exactly as many tasks scheduled in
694 // fd_controller_maps_[0|1] as there are read/write fds that we're tracking.
695 for (int fd = 0; fd <= fd_max; ++fd) {
696 // Note that fd_exc is unused in the current version of libcurl so is_exc
697 // should always be false.
698 bool is_exc = FD_ISSET(fd, &fd_exc) != 0;
699 bool must_track[2] = {
700 is_exc || (FD_ISSET(fd, &fd_read) != 0), // track 0 -- read
701 is_exc || (FD_ISSET(fd, &fd_write) != 0) // track 1 -- write
702 };
703
704 for (size_t t = 0; t < base::size(fd_controller_maps_); ++t) {
705 bool tracked =
706 fd_controller_maps_[t].find(fd) != fd_controller_maps_[t].end();
707
708 if (!must_track[t]) {
709 // If we have an outstanding io_channel, remove it.
710 fd_controller_maps_[t].erase(fd);
711 continue;
712 }
713
714 // If we are already tracking this fd, continue -- nothing to do.
715 if (tracked)
716 continue;
717
718 // Track a new fd.
719 switch (t) {
720 case 0: // Read
721 fd_controller_maps_[t][fd] =
722 base::FileDescriptorWatcher::WatchReadable(
723 fd,
724 base::BindRepeating(&LibcurlHttpFetcher::CurlPerformOnce,
725 base::Unretained(this)));
726 break;
727 case 1: // Write
728 fd_controller_maps_[t][fd] =
729 base::FileDescriptorWatcher::WatchWritable(
730 fd,
731 base::BindRepeating(&LibcurlHttpFetcher::CurlPerformOnce,
732 base::Unretained(this)));
733 }
734 static int io_counter = 0;
735 io_counter++;
736 if (io_counter % 50 == 0) {
737 LOG(INFO) << "io_counter = " << io_counter;
738 }
739 }
740 }
741
742 // Set up a timeout callback for libcurl.
743 if (timeout_id_ == MessageLoop::kTaskIdNull) {
744 VLOG(1) << "Setting up timeout source: " << idle_seconds_ << " seconds.";
745 timeout_id_ = MessageLoop::current()->PostDelayedTask(
746 FROM_HERE,
747 base::Bind(&LibcurlHttpFetcher::TimeoutCallback,
748 base::Unretained(this)),
749 TimeDelta::FromSeconds(idle_seconds_));
750 }
751 }
752
RetryTimeoutCallback()753 void LibcurlHttpFetcher::RetryTimeoutCallback() {
754 retry_task_id_ = MessageLoop::kTaskIdNull;
755 if (transfer_paused_) {
756 restart_transfer_on_unpause_ = true;
757 return;
758 }
759 ResumeTransfer(url_);
760 CurlPerformOnce();
761 }
762
TimeoutCallback()763 void LibcurlHttpFetcher::TimeoutCallback() {
764 // We always re-schedule the callback, even if we don't want to be called
765 // anymore. We will remove the event source separately if we don't want to
766 // be called back.
767 timeout_id_ = MessageLoop::current()->PostDelayedTask(
768 FROM_HERE,
769 base::Bind(&LibcurlHttpFetcher::TimeoutCallback, base::Unretained(this)),
770 TimeDelta::FromSeconds(idle_seconds_));
771
772 // CurlPerformOnce() may call CleanUp(), so we need to schedule our callback
773 // first, since it could be canceled by this call.
774 if (transfer_in_progress_)
775 CurlPerformOnce();
776 }
777
CleanUp()778 void LibcurlHttpFetcher::CleanUp() {
779 MessageLoop::current()->CancelTask(retry_task_id_);
780 retry_task_id_ = MessageLoop::kTaskIdNull;
781
782 MessageLoop::current()->CancelTask(timeout_id_);
783 timeout_id_ = MessageLoop::kTaskIdNull;
784
785 for (size_t t = 0; t < base::size(fd_controller_maps_); ++t) {
786 fd_controller_maps_[t].clear();
787 }
788
789 if (curl_http_headers_) {
790 curl_slist_free_all(curl_http_headers_);
791 curl_http_headers_ = nullptr;
792 }
793 if (curl_handle_) {
794 if (curl_multi_handle_) {
795 CHECK_EQ(curl_multi_remove_handle(curl_multi_handle_, curl_handle_),
796 CURLM_OK);
797 }
798 curl_easy_cleanup(curl_handle_);
799 curl_handle_ = nullptr;
800 }
801 if (curl_multi_handle_) {
802 CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK);
803 curl_multi_handle_ = nullptr;
804 }
805 transfer_in_progress_ = false;
806 transfer_paused_ = false;
807 restart_transfer_on_unpause_ = false;
808 }
809
GetHttpResponseCode()810 void LibcurlHttpFetcher::GetHttpResponseCode() {
811 long http_response_code = 0; // NOLINT(runtime/int) - curl needs long.
812 if (base::StartsWith(url_, "file://", base::CompareCase::INSENSITIVE_ASCII)) {
813 // Fake out a valid response code for file:// URLs.
814 http_response_code_ = 299;
815 } else if (curl_easy_getinfo(curl_handle_,
816 CURLINFO_RESPONSE_CODE,
817 &http_response_code) == CURLE_OK) {
818 http_response_code_ = static_cast<int>(http_response_code);
819 } else {
820 LOG(ERROR) << "Unable to get http response code from curl_easy_getinfo";
821 }
822 }
823
GetCurlCode()824 CURLcode LibcurlHttpFetcher::GetCurlCode() {
825 CURLcode curl_code = CURLE_OK;
826 while (true) {
827 // Repeated calls to |curl_multi_info_read| will return a new struct each
828 // time, until a NULL is returned as a signal that there is no more to get
829 // at this point.
830 int msgs_in_queue{};
831 CURLMsg* curl_msg =
832 curl_multi_info_read(curl_multi_handle_, &msgs_in_queue);
833 if (curl_msg == nullptr)
834 break;
835 // When |curl_msg| is |CURLMSG_DONE|, a transfer of an easy handle is done,
836 // and then data contains the return code for this transfer.
837 if (curl_msg->msg == CURLMSG_DONE) {
838 // Make sure |curl_multi_handle_| has one and only one easy handle
839 // |curl_handle_|.
840 CHECK_EQ(curl_handle_, curl_msg->easy_handle);
841 // Transfer return code reference:
842 // https://curl.haxx.se/libcurl/c/libcurl-errors.html
843 curl_code = curl_msg->data.result;
844 }
845 }
846
847 // Gets connection error if exists.
848 long connect_error = 0; // NOLINT(runtime/int) - curl needs long.
849 CURLcode res =
850 curl_easy_getinfo(curl_handle_, CURLINFO_OS_ERRNO, &connect_error);
851 if (res == CURLE_OK && connect_error) {
852 LOG(ERROR) << "Connect error code from the OS: " << connect_error;
853 }
854
855 return curl_code;
856 }
857
UpdateState(bool failed_to_resolve_host)858 void UnresolvedHostStateMachine::UpdateState(bool failed_to_resolve_host) {
859 switch (state_) {
860 case State::kInit:
861 if (failed_to_resolve_host) {
862 state_ = State::kRetry;
863 }
864 break;
865 case State::kRetry:
866 if (failed_to_resolve_host) {
867 state_ = State::kNotRetry;
868 } else {
869 state_ = State::kRetriedSuccess;
870 }
871 break;
872 case State::kNotRetry:
873 break;
874 case State::kRetriedSuccess:
875 break;
876 default:
877 NOTREACHED();
878 break;
879 }
880 }
881
882 } // namespace chromeos_update_engine
883