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

Side by Side Diff: components/safe_browsing_db/v4_get_hash_protocol_manager.h

Issue 2233103002: Move full hash caching logic to v4_get_hash_protocol_manager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Bring back the histogram to check if there were any hits in the response from the server Created 4 years, 3 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 #ifndef COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_ 5 #ifndef COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
6 #define COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_ 6 #define COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
7 7
8 // A class that implements Chrome's interface with the SafeBrowsing V4 protocol. 8 // A class that implements Chrome's interface with the SafeBrowsing V4 protocol.
9 // 9 //
10 // The V4GetHashProtocolManager handles formatting and making requests of, and 10 // The V4GetHashProtocolManager handles formatting and making requests of, and
(...skipping 19 matching lines...) Expand all
30 30
31 class GURL; 31 class GURL;
32 32
33 namespace net { 33 namespace net {
34 class URLFetcher; 34 class URLFetcher;
35 class URLRequestContextGetter; 35 class URLRequestContextGetter;
36 } // namespace net 36 } // namespace net
37 37
38 namespace safe_browsing { 38 namespace safe_browsing {
39 39
40 // The matching hash prefixes and corresponding stores, for each full hash
41 // generated for a given URL.
42 typedef base::hash_map<FullHash, StoreAndHashPrefixes>
43 FullHashToStoreAndHashPrefixesMap;
44
45 // ----------------------------------------------------------------
46
47 // All information about a particular full hash i.e. negative TTL, store for
48 // which it is valid, and metadata associated with that store.
49 struct FullHashInfo {
50 public:
51 FullHash full_hash;
52
53 // The list for which this full hash is applicable.
54 UpdateListIdentifier list_id;
55
56 // The expiration time of the full hash for a particular store.
57 base::Time positive_ttl;
Scott Hess - ex-Googler 2016/09/12 23:31:35 I'm slightly unnerved by this use of "ttl" with an
vakh (use Gerrit instead) 2016/09/13 00:12:07 I understand your concern but I feel like there ar
Scott Hess - ex-Googler 2016/09/13 00:31:34 Sometimes the code uses "expire" for this concept,
vakh (use Gerrit instead) 2016/09/13 01:18:56 Done.
58
59 // Any metadata for this full hash for a particular store.
60 ThreatMetadata metadata;
61
62 FullHashInfo(const FullHash& full_hash,
63 const UpdateListIdentifier& list_id,
64 const base::Time& positive_ttl);
65 FullHashInfo(const FullHashInfo& other);
66 ~FullHashInfo();
67
68 bool operator==(const FullHashInfo& other) const;
69 bool operator!=(const FullHashInfo& other) const;
70
71 private:
72 FullHashInfo();
73 };
74
75 // Caches individual response from GETHASH response.
76 struct CachedHashPrefixInfo {
77 // The negative TTL for the hash prefix that leads to this
78 // CachedHashPrefixInfo. The client should not send any more requests for that
79 // hash prefix until this time.
80 base::Time negative_ttl;
81
82 // The list of all full hashes (and related info) that start with a
83 // particular hash prefix and are known to be unsafe.
84 std::vector<FullHashInfo> full_hash_infos;
85
86 CachedHashPrefixInfo();
87 CachedHashPrefixInfo(const CachedHashPrefixInfo& other);
88 ~CachedHashPrefixInfo();
89 };
90
91 // Cached full hashes received from the server for the corresponding hash
92 // prefixes.
93 typedef base::hash_map<HashPrefix, CachedHashPrefixInfo> FullHashCache;
94
95 // FullHashCallback is invoked when GetFullHashes completes.
96 // Parameters:
97 // - The vector of full hash results. If empty, indicates that there
98 // were no matches, and that the resource is safe.
Scott Hess - ex-Googler 2016/09/12 23:31:35 Is this going to acquire additional parameters? O
vakh (use Gerrit instead) 2016/09/13 00:12:07 Are you asking this because of "Parameters:"? I th
Scott Hess - ex-Googler 2016/09/13 00:31:34 Mostly because the comment is setup like "Here's t
vakh (use Gerrit instead) 2016/09/13 01:18:56 Done.
vakh (use Gerrit instead) 2016/09/13 01:18:56 Done.
99 typedef base::Callback<void(const std::vector<FullHashInfo>&)> FullHashCallback;
100
101 // Information needed to update the cache and call the callback to post the
102 // results.
103 struct FullHashCallbackInfo {
104 FullHashCallbackInfo();
105 FullHashCallbackInfo(const std::vector<FullHashInfo>& cached_full_hash_infos,
106 const std::vector<HashPrefix>& prefixes_requested,
107 std::unique_ptr<net::URLFetcher> fetcher,
108 const FullHashToStoreAndHashPrefixesMap&
109 full_hash_to_store_and_hash_prefixes,
110 const FullHashCallback& callback);
111 ~FullHashCallbackInfo();
112
113 // The FullHashInfo objects retrieved from cache. These are merged with the
114 // results received from the server before invoking the callback.
115 std::vector<FullHashInfo> cached_full_hash_infos;
116
117 // The callback method to call after collecting the full hashes for given
118 // hash prefixes.
119 FullHashCallback callback;
120
121 // The fetcher that will return the response from the server. This is stored
122 // here as a unique pointer to be able to reason about its lifetime easily.
123 std::unique_ptr<net::URLFetcher> fetcher;
124
125 // The generated full hashes and the corresponding prefixes and the stores in
126 // which to look for a full hash match.
127 FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
128
129 // The prefixes that were requested from the server.
130 std::vector<HashPrefix> prefixes_requested;
131 };
132
133 // ----------------------------------------------------------------
134
40 class V4GetHashProtocolManagerFactory; 135 class V4GetHashProtocolManagerFactory;
41 136
42 class V4GetHashProtocolManager : public net::URLFetcherDelegate, 137 class V4GetHashProtocolManager : public net::URLFetcherDelegate,
43 public base::NonThreadSafe { 138 public base::NonThreadSafe {
44 public: 139 public:
45 // FullHashCallback is invoked when GetFullHashes completes. 140 // Invoked when GetFullHashesWithApis completes.
46 // Parameters: 141 // Parameters:
47 // - The vector of full hash results. If empty, indicates that there 142 // - The API threat metadata for the given URL.
48 // were no matches, and that the resource is safe. 143 typedef base::Callback<void(const ThreatMetadata& md)>
49 // - The negative cache expire time of the result. This value may be 144 ThreatMetadataForApiCallback;
50 // uninitialized, and the results should not be cached in this case.
51 typedef base::Callback<void(const std::vector<SBFullHashResult>&,
52 const base::Time&)>
53 FullHashCallback;
54 145
55 ~V4GetHashProtocolManager() override; 146 ~V4GetHashProtocolManager() override;
56 147
148 // Create an instance of the safe browsing v4 protocol manager.
149 static std::unique_ptr<V4GetHashProtocolManager> Create(
150 net::URLRequestContextGetter* request_context_getter,
151 const base::hash_set<UpdateListIdentifier>& stores_to_request,
152 const V4ProtocolConfig& config);
153
57 // Makes the passed |factory| the factory used to instantiate 154 // Makes the passed |factory| the factory used to instantiate
58 // a V4GetHashProtocolManager. Useful for tests. 155 // a V4GetHashProtocolManager. Useful for tests.
59 static void RegisterFactory( 156 static void RegisterFactory(
60 std::unique_ptr<V4GetHashProtocolManagerFactory> factory); 157 std::unique_ptr<V4GetHashProtocolManagerFactory> factory);
61 158
62 // Create an instance of the safe browsing v4 protocol manager. 159 // Empties the cache.
63 static V4GetHashProtocolManager* Create( 160 void ClearCache();
64 net::URLRequestContextGetter* request_context_getter,
65 const V4ProtocolConfig& config);
66
67 // net::URLFetcherDelegate interface.
68 void OnURLFetchComplete(const net::URLFetcher* source) override;
69 161
70 // Retrieve the full hash for a set of prefixes, and invoke the callback 162 // Retrieve the full hash for a set of prefixes, and invoke the callback
71 // argument when the results are retrieved. The callback may be invoked 163 // argument when the results are retrieved. The callback may be invoked
72 // synchronously. 164 // synchronously.
73 virtual void GetFullHashes(const std::vector<SBPrefix>& prefixes, 165 virtual void GetFullHashes(const FullHashToStoreAndHashPrefixesMap&
74 const std::vector<PlatformType>& platforms, 166 full_hash_to_matching_hash_prefixes,
75 ThreatType threat_type,
76 FullHashCallback callback); 167 FullHashCallback callback);
77 168
78 // Retrieve the full hash and API metadata for a set of prefixes, and invoke 169 // Retrieve the full hash and API metadata for a URL, and invoke the callback
79 // the callback argument when the results are retrieved. The callback may be 170 // argument when the results are retrieved. The callback may be invoked
80 // invoked synchronously. 171 // synchronously.
81 virtual void GetFullHashesWithApis(const std::vector<SBPrefix>& prefixes, 172 // GetFullHashesWithApis is a special case of GetFullHashes. It is here
82 FullHashCallback callback); 173 // primarily for legacy reasons: so that DatabaseManager, which speaks PVer3,
83 174 // and V4LocalDatabaseManager, which speaks PVer4, can both use this class to
84 // Overrides the clock used to check the time. 175 // perform API lookups. Once PVer4 migration is complete, DatabaseManager
85 void SetClockForTests(std::unique_ptr<base::Clock> clock); 176 // should be deleted and then this method can be moved to the
177 // V4LocalDatabaseManager class.
178 // TODO(vakh): Move this method to V4LocalDatabaseManager after launching
179 // PVer4 in Chromium.
180 virtual void GetFullHashesWithApis(const GURL& url,
181 ThreatMetadataForApiCallback api_callback);
182
183 // net::URLFetcherDelegate interface.
184 void OnURLFetchComplete(const net::URLFetcher* source) override;
86 185
87 protected: 186 protected:
88 // Constructs a V4GetHashProtocolManager that issues 187 // Constructs a V4GetHashProtocolManager that issues
89 // network requests using |request_context_getter|. 188 // network requests using |request_context_getter|.
90 V4GetHashProtocolManager(net::URLRequestContextGetter* request_context_getter, 189 V4GetHashProtocolManager(
91 const V4ProtocolConfig& config); 190 net::URLRequestContextGetter* request_context_getter,
191 const base::hash_set<UpdateListIdentifier>& stores_to_request,
192 const V4ProtocolConfig& config);
92 193
93 private: 194 private:
94 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 195 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestGetHashRequest);
95 TestGetHashRequest); 196 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestParseHashResponse);
96 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 197 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
97 TestParseHashResponse);
98 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest,
99 TestParseHashResponseWrongThreatEntryType); 198 TestParseHashResponseWrongThreatEntryType);
100 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 199 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
101 TestParseHashThreatPatternType); 200 TestParseHashThreatPatternType);
102 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 201 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
103 TestParseHashResponseNonPermissionMetadata); 202 TestParseHashResponseNonPermissionMetadata);
104 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 203 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
105 TestParseHashResponseInconsistentThreatTypes); 204 TestParseHashResponseInconsistentThreatTypes);
106 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 205 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
107 TestGetHashErrorHandlingOK); 206 TestGetHashErrorHandlingOK);
108 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 207 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
208 TestResultsNotCachedForNegativeCacheDuration);
209 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
109 TestGetHashErrorHandlingNetwork); 210 TestGetHashErrorHandlingNetwork);
110 FRIEND_TEST_ALL_PREFIXES(SafeBrowsingV4GetHashProtocolManagerTest, 211 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
111 TestGetHashErrorHandlingResponseCode); 212 TestGetHashErrorHandlingResponseCode);
213 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, GetCachedResults);
214 FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestUpdatesAreMerged);
215 friend class V4GetHashProtocolManagerTest;
112 friend class V4GetHashProtocolManagerFactoryImpl; 216 friend class V4GetHashProtocolManagerFactoryImpl;
113 217
218 FullHashCache* full_hash_cache_for_tests() { return &full_hash_cache_; }
219
220 // Looks up the cached results for full hashes in
221 // |full_hash_to_store_and_hash_prefixes|. Fills |prefixes_to_request| with
222 // the prefixes that need to be requested. Fills |cached_full_hash_infos|
223 // with the cached results.
224 // Note: It is valid for both |prefixes_to_request| and
225 // |cached_full_hash_infos| to be empty after this function finishes.
226 void GetFullHashCachedResults(
227 const FullHashToStoreAndHashPrefixesMap&
228 full_hash_to_store_and_hash_prefixes,
229 const base::Time& now,
230 std::vector<HashPrefix>* prefixes_to_request,
231 std::vector<FullHashInfo>* cached_full_hash_infos) const;
232
233 // Fills a FindFullHashesRequest protocol buffer for a request.
234 // Returns the serialized and base 64 encoded request as a string.
235 std::string GetHashRequest(
236 const std::vector<HashPrefix>& prefixes_to_request);
237
114 void GetHashUrlAndHeaders(const std::string& request_base64, 238 void GetHashUrlAndHeaders(const std::string& request_base64,
115 GURL* gurl, 239 GURL* gurl,
116 net::HttpRequestHeaders* headers) const; 240 net::HttpRequestHeaders* headers) const;
117 241
118 // Fills a FindFullHashesRequest protocol buffer for a request.
119 // Returns the serialized and base 64 encoded request as a string.
120 std::string GetHashRequest(const std::vector<SBPrefix>& prefixes,
121 const std::vector<PlatformType>& platforms,
122 ThreatType threat_type);
123
124 // Parses a FindFullHashesResponse protocol buffer and fills the results in
125 // |full_hashes| and |negative_cache_expire|. |data| is a serialized
126 // FindFullHashes protocol buffer. |negative_cache_expire| is the cache expiry
127 // time of the response for entities that did not match the threat list.
128 // Returns true if parsing is successful, false otherwise.
129 bool ParseHashResponse(const std::string& data_base64,
130 std::vector<SBFullHashResult>* full_hashes,
131 base::Time* negative_cache_expire);
132
133 // Resets the gethash error counter and multiplier.
134 void ResetGetHashErrors();
135
136 // Updates internal state for each GetHash response error, assuming that 242 // Updates internal state for each GetHash response error, assuming that
137 // the current time is |now|. 243 // the current time is |now|.
138 void HandleGetHashError(const base::Time& now); 244 void HandleGetHashError(const base::Time& now);
139 245
246 // Merges the results from the cache and the results from the server. The
247 // response from the server may include information for full hashes from
248 // stores other than those required by this client so it filters out those
249 // results that the client did not ask for.
250 void MergeResults(const FullHashToStoreAndHashPrefixesMap&
251 full_hash_to_store_and_hash_prefixes,
252 const std::vector<FullHashInfo>& full_hash_infos,
253 std::vector<FullHashInfo>* merged_full_hash_infos);
254
255 // Calls |api_callback| with an object of ThreatMetadata that contains
256 // permission API metadata for full hashes in those |full_hash_infos| that
257 // have a full hash in |full_hashes|.
258 void OnFullHashForApi(const ThreatMetadataForApiCallback& api_callback,
259 const base::hash_set<FullHash>& full_hashes,
260 const std::vector<FullHashInfo>& full_hash_infos);
261
262 // Parses a FindFullHashesResponse protocol buffer and fills the results in
263 // |full_hash_infos| and |negative_cache_expire|. |response_data| is a
264 // serialized FindFullHashes protocol buffer. |negative_cache_expire| is the
265 // cache expiry time of the hash prefixes that were requested. Returns true if
266 // parsing is successful; false otherwise.
267 bool ParseHashResponse(const std::string& response_data,
268 std::vector<FullHashInfo>* full_hash_infos,
269 base::Time* negative_cache_expire);
270
271 // Parses the store specific |metadata| information from |match|. Returns
272 // true if the metadata information was parsed correctly and was consistent
273 // with what's expected from that corresponding store; false otherwise.
274 bool ParseMetadata(const ThreatMatch& match, ThreatMetadata* metadata);
275
276 // Resets the gethash error counter and multiplier.
277 void ResetGetHashErrors();
278
279 // Overrides the clock used to check the time.
280 void SetClockForTests(std::unique_ptr<base::Clock> clock);
281
282 // Updates the state of the full hash cache upon receiving a valid response
283 // from the server.
284 void UpdateCache(const std::vector<HashPrefix>& prefixes_requested,
285 const std::vector<FullHashInfo>& full_hash_infos,
286 const base::Time& negative_cache_expire);
287
140 private: 288 private:
141 // Map of GetHash requests to parameters which created it. 289 // Map of GetHash requests to parameters which created it.
142 using HashRequests = base::hash_map< 290 using PendingHashRequests =
143 const net::URLFetcher*, 291 base::hash_map<const net::URLFetcher*,
144 std::pair<std::unique_ptr<net::URLFetcher>, FullHashCallback>>; 292 std::unique_ptr<FullHashCallbackInfo>>;
145 293
146 // The factory that controls the creation of V4GetHashProtocolManager. 294 // The factory that controls the creation of V4GetHashProtocolManager.
147 // This is used by tests. 295 // This is used by tests.
148 static V4GetHashProtocolManagerFactory* factory_; 296 static V4GetHashProtocolManagerFactory* factory_;
149 297
150 // Current active request (in case we need to cancel) for updates or chunks 298 // Current active request (in case we need to cancel) for updates or chunks
151 // from the SafeBrowsing service. We can only have one of these outstanding 299 // from the SafeBrowsing service. We can only have one of these outstanding
152 // at any given time unlike GetHash requests, which are tracked separately. 300 // at any given time unlike GetHash requests, which are tracked separately.
153 std::unique_ptr<net::URLFetcher> request_; 301 std::unique_ptr<net::URLFetcher> request_;
154 302
155 // The number of HTTP response errors since the the last successful HTTP 303 // The number of HTTP response errors since the the last successful HTTP
156 // response, used for request backoff timing. 304 // response, used for request backoff timing.
157 size_t gethash_error_count_; 305 size_t gethash_error_count_;
158 306
159 // Multiplier for the backoff error after the second. 307 // Multiplier for the backoff error after the second.
160 size_t gethash_back_off_mult_; 308 size_t gethash_back_off_mult_;
161 309
162 HashRequests hash_requests_; 310 PendingHashRequests pending_hash_requests_;
163 311
164 // For v4, the next gethash time is set to the backoff time is the last 312 // For v4, the next gethash time is set to the backoff time is the last
165 // response was an error, or the minimum wait time if the last response was 313 // response was an error, or the minimum wait time if the last response was
166 // successful. 314 // successful.
167 base::Time next_gethash_time_; 315 base::Time next_gethash_time_;
168 316
169 // The config of the client making Pver4 requests. 317 // The config of the client making Pver4 requests.
170 const V4ProtocolConfig config_; 318 const V4ProtocolConfig config_;
171 319
172 // The context we use to issue network requests. 320 // The context we use to issue network requests.
173 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; 321 scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
174 322
175 // ID for URLFetchers for testing. 323 // ID for URLFetchers for testing.
176 int url_fetcher_id_; 324 int url_fetcher_id_;
177 325
178 // The clock used to vend times. 326 // The clock used to vend times.
179 std::unique_ptr<base::Clock> clock_; 327 std::unique_ptr<base::Clock> clock_;
180 328
329 // A cache of full hash results.
330 FullHashCache full_hash_cache_;
331
332 // The following sets represent the combination of lists that we would always
333 // request from the server, irrespective of which list we found the hash
334 // prefix match in.
335 base::hash_set<PlatformType> platform_types_;
336 base::hash_set<ThreatEntryType> threat_entry_types_;
337 base::hash_set<ThreatType> threat_types_;
338
181 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManager); 339 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManager);
182 }; 340 };
183 341
184 // Interface of a factory to create V4GetHashProtocolManager. Useful for tests. 342 // Interface of a factory to create V4GetHashProtocolManager. Useful for tests.
185 class V4GetHashProtocolManagerFactory { 343 class V4GetHashProtocolManagerFactory {
186 public: 344 public:
187 V4GetHashProtocolManagerFactory() {} 345 V4GetHashProtocolManagerFactory() {}
188 virtual ~V4GetHashProtocolManagerFactory() {} 346 virtual ~V4GetHashProtocolManagerFactory() {}
189 virtual V4GetHashProtocolManager* CreateProtocolManager( 347 virtual std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
190 net::URLRequestContextGetter* request_context_getter, 348 net::URLRequestContextGetter* request_context_getter,
349 const base::hash_set<UpdateListIdentifier>& stores_to_request,
191 const V4ProtocolConfig& config) = 0; 350 const V4ProtocolConfig& config) = 0;
192 351
193 private: 352 private:
194 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactory); 353 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactory);
195 }; 354 };
196 355
356 #ifndef NDEBUG
357 std::ostream& operator<<(std::ostream& os, const FullHashInfo& id);
358 #endif
359
197 } // namespace safe_browsing 360 } // namespace safe_browsing
198 361
199 #endif // COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_ 362 #endif // COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698