Chromium Code Reviews| Index: components/network_time/network_time_tracker.cc |
| diff --git a/components/network_time/network_time_tracker.cc b/components/network_time/network_time_tracker.cc |
| index e3dc58ecd754f6ab26550ecb56db4d9a9425a092..9ec2b1897b90cba10c55a7d2d1196d8e3856ac8e 100644 |
| --- a/components/network_time/network_time_tracker.cc |
| +++ b/components/network_time/network_time_tracker.cc |
| @@ -8,13 +8,19 @@ |
| #include <utility> |
| #include "base/i18n/time_formatting.h" |
| +#include "base/json/json_reader.h" |
| #include "base/logging.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/tick_clock.h" |
| #include "build/build_config.h" |
| +#include "components/client_update_protocol/ecdsa.h" |
| #include "components/network_time/network_time_pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| +#include "net/base/load_flags.h" |
| +#include "net/http/http_response_headers.h" |
| +#include "net/url_request/url_fetcher.h" |
| +#include "url/gurl.h" |
| namespace network_time { |
| @@ -41,6 +47,34 @@ const char kPrefUncertainty[] = "uncertainty"; |
| // Name of a pref that stores the network time via |ToJsTime|. |
| const char kPrefNetworkTime[] = "network"; |
| +// DO NOT SUBMIT |
| +const char kTimeServiceURL[] = "http://localhost:6125/time/1/current"; |
| + |
| +// This is an ECDSA prime256v1 named-curve key. |
| +// DO NOT SUBMIT. |
| +const int kKeyVersion = 2; |
| +const uint8_t kKeyPubBytes[] = { |
| + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, |
| + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, |
| + 0x42, 0x00, 0x04, 0xe0, 0x6b, 0x0d, 0x76, 0x75, 0xa3, 0x99, 0x7d, 0x7c, |
| + 0x1b, 0xd6, 0x3c, 0x73, 0xbb, 0x4b, 0xfe, 0x0a, 0xe7, 0x2f, 0x61, 0x3d, |
| + 0x77, 0x0a, 0xaa, 0x14, 0xd8, 0x5a, 0xbf, 0x14, 0x60, 0xec, 0xf6, 0x32, |
| + 0x77, 0xb5, 0xa7, 0xe6, 0x35, 0xa5, 0x61, 0xaf, 0xdc, 0xdf, 0x91, 0xce, |
| + 0x45, 0x34, 0x5f, 0x36, 0x85, 0x2f, 0xb9, 0x53, 0x00, 0x5d, 0x86, 0xe7, |
| + 0x04, 0x16, 0xe2, 0x3d, 0x21, 0x76, 0x2b}; |
| + |
| +static std::string GetServerETag(const net::URLFetcher* source) { |
| + const net::HttpResponseHeaders* response_headers = |
| + source->GetResponseHeaders(); |
| + if (response_headers == nullptr) { |
| + return std::string(); |
| + } |
| + std::string etag; |
| + return response_headers->EnumerateHeader(nullptr, "ETag", &etag) |
| + ? etag |
| + : std::string(); |
| +} |
| + |
| } // namespace |
| // static |
| @@ -51,8 +85,10 @@ void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { |
| NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::Clock> clock, |
| scoped_ptr<base::TickClock> tick_clock, |
| - PrefService* pref_service) |
| - : clock_(std::move(clock)), |
| + PrefService* pref_service, |
| + net::URLRequestContextGetter* getter) |
| + : getter_(getter), |
| + clock_(std::move(clock)), |
| tick_clock_(std::move(tick_clock)), |
| pref_service_(pref_service) { |
|
estark
2016/03/28 23:18:59
Initialize |query_signer_| and |time_fetcher_| to
mab
2016/03/29 03:13:03
Whoa. Surprised that's necessary for scoped_ptr.
|
| const base::DictionaryValue* time_mapping = |
| @@ -82,12 +118,102 @@ NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::Clock> clock, |
| pref_service_->ClearPref(prefs::kNetworkTimeMapping); |
| network_time_at_last_measurement_ = base::Time(); // Reset. |
| } |
| + if (getter != nullptr) { |
| + query_signer_ = client_update_protocol::Ecdsa::Create( |
| + kKeyVersion, |
| + {reinterpret_cast<const char*>(kKeyPubBytes), sizeof(kKeyPubBytes)}); |
| + base::TimeDelta period = base::TimeDelta::FromMinutes(60); |
| + query_timer_.Start(FROM_HERE, period, this, |
| + &NetworkTimeTracker::QueryTimeService); |
| + } |
| } |
| NetworkTimeTracker::~NetworkTimeTracker() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| +void NetworkTimeTracker::QueryTimeService() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // If GetNetworkTime() returns true, the NetworkTimeTracker thinks |
| + // it is in sync, so there is no need to query. |
| + base::Time network_time; |
| + if (GetNetworkTime(&network_time, nullptr)) { |
| + return; |
| + } |
| + |
| + std::string query_string; |
| + query_signer_->SignRequest({"", 0}, &query_string); |
| + GURL url(kTimeServiceURL); |
| + GURL::Replacements replacements; |
| + replacements.SetQueryStr(query_string); |
| + url = url.ReplaceComponents(replacements); |
| + |
| + // This cancels any outstanding fetch. |
| + time_fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this); |
| + if (time_fetcher_ == nullptr) { |
| + VLOG(1) << "tried to make fetch happen; failed"; |
| + return; |
| + } |
| + time_fetcher_->SetRequestContext(getter_); |
| + // Not expecting any cookies, but just in case. |
| + time_fetcher_->SetLoadFlags( |
| + net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SEND_COOKIES | |
| + net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_BYPASS_CACHE | |
| + net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| + time_fetcher_->Start(); |
| + fetch_started_ = tick_clock_->NowTicks(); |
| +} |
| + |
| +void NetworkTimeTracker::OnURLFetchComplete(const net::URLFetcher* source) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(source); |
| + if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS && |
| + source->GetResponseCode() != 200) { |
| + VLOG(1) << "fetch failed, status=" << source->GetStatus().status() |
| + << ",code=" << source->GetResponseCode(); |
| + return; |
| + } |
| + |
| + std::string response_body; |
| + if (!source->GetResponseAsString(&response_body)) { |
| + VLOG(1) << "failed to get response"; |
| + return; |
| + } |
| + DCHECK(query_signer_.get()); |
| + if (!query_signer_->ValidateResponse(response_body, GetServerETag(source))) { |
| + VLOG(1) << "invalid signature"; |
| + return; |
| + } |
| + response_body = response_body.substr(5); // Skips leading )]}'\n |
| + base::JSONReader reader; |
| + scoped_ptr<base::Value> value = reader.Read(response_body); |
| + if (value == nullptr) { |
| + VLOG(1) << "bad JSON"; |
| + return; |
| + } |
| + const base::DictionaryValue* dict; |
| + if (!value->GetAsDictionary(&dict)) { |
| + VLOG(1) << "not a dictionary"; |
| + return; |
| + } |
| + double current_time_millis; |
| + if (!dict->GetDouble("current_time_millis", ¤t_time_millis)) { |
| + VLOG(1) << "no current_time_millis"; |
| + return; |
| + } |
| + // There's also a "server_nonce" key, which we can ignore. |
| + base::Time current_time = base::Time::FromJsTime(current_time_millis); |
| + // The extra 10 seconds comes from a property of the time server that we |
| + // happen to know, which its maximum allowable clock skew. It's unlikely that |
| + // it would ever be that badly wrong, but all the same it's included here to |
| + // document the very rough nature of the time service provided by this class. |
| + base::TimeDelta resolution = |
| + base::TimeDelta::FromMilliseconds(1) + base::TimeDelta::FromSeconds(10); |
| + base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_; |
| + UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks()); |
| +} |
| + |
| void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, |
| base::TimeDelta resolution, |
| base::TimeDelta latency, |
| @@ -131,6 +257,8 @@ void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, |
| time_mapping.SetDouble(kPrefNetworkTime, |
| network_time_at_last_measurement_.ToJsTime()); |
| pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); |
| + |
| + query_timer_.Reset(); |
| } |
| bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, |