1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
6 
7 #include <algorithm>
8 
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/net/system_network_context_manager.h"
19 #include "chromeos/constants/chromeos_switches.h"
20 #include "chromeos/dbus/dbus_thread_manager.h"
21 #include "chromeos/dbus/shill/shill_profile_client.h"
22 #include "chromeos/login/login_state/login_state.h"
23 #include "chromeos/network/network_event_log.h"
24 #include "chromeos/network/network_state.h"
25 #include "chromeos/network/network_state_handler.h"
26 #include "content/public/browser/notification_service.h"
27 #include "net/http/http_status_code.h"
28 #include "net/traffic_annotation/network_traffic_annotation.h"
29 #include "services/network/public/cpp/shared_url_loader_factory.h"
30 #include "third_party/cros_system_api/dbus/service_constants.h"
31 
32 using captive_portal::CaptivePortalDetector;
33 
34 namespace chromeos {
35 
36 namespace {
37 
38 // Delay before portal detection caused by changes in proxy settings.
39 constexpr int kProxyChangeDelaySec = 1;
40 
41 // Maximum number of reports from captive portal detector about
42 // offline state in a row before notification is sent to observers.
43 constexpr int kMaxOfflineResultsBeforeReport = 3;
44 
45 // Delay before portal detection attempt after !ONLINE -> !ONLINE
46 // transition.
47 constexpr int kShortInitialDelayBetweenAttemptsMs = 600;
48 
49 // Maximum timeout before portal detection attempts after !ONLINE ->
50 // !ONLINE transition.
51 constexpr int kShortMaximumDelayBetweenAttemptsMs = 2 * 60 * 1000;
52 
53 // Delay before portal detection attempt after !ONLINE -> ONLINE
54 // transition.
55 constexpr int kLongInitialDelayBetweenAttemptsMs = 30 * 1000;
56 
57 // Maximum timeout before portal detection attempts after !ONLINE ->
58 // ONLINE transition.
59 constexpr int kLongMaximumDelayBetweenAttemptsMs = 5 * 60 * 1000;
60 
DefaultNetwork()61 const NetworkState* DefaultNetwork() {
62   return NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
63 }
64 
SetNetworkPortalDetected(const NetworkState * network,bool portal_detected)65 void SetNetworkPortalDetected(const NetworkState* network,
66                               bool portal_detected) {
67   NetworkHandler::Get()
68       ->network_state_handler()
69       ->SetNetworkChromePortalDetected(network->path(), portal_detected);
70 }
71 
72 }  // namespace
73 
74 ////////////////////////////////////////////////////////////////////////////////
75 // NetworkPortalDetectorImpl, public:
76 
NetworkPortalDetectorImpl(network::mojom::URLLoaderFactory * loader_factory_for_testing)77 NetworkPortalDetectorImpl::NetworkPortalDetectorImpl(
78     network::mojom::URLLoaderFactory* loader_factory_for_testing)
79     : strategy_(PortalDetectorStrategy::CreateById(
80           PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN,
81           this)) {
82   NET_LOG(EVENT) << "NetworkPortalDetectorImpl::NetworkPortalDetectorImpl()";
83   network::mojom::URLLoaderFactory* loader_factory;
84   if (loader_factory_for_testing) {
85     loader_factory = loader_factory_for_testing;
86   } else {
87     shared_url_loader_factory_ =
88         g_browser_process->system_network_context_manager()
89             ->GetSharedURLLoaderFactory();
90     loader_factory = shared_url_loader_factory_.get();
91   }
92   captive_portal_detector_.reset(new CaptivePortalDetector(loader_factory));
93 
94   registrar_.Add(this, chrome::NOTIFICATION_AUTH_SUPPLIED,
95                  content::NotificationService::AllSources());
96   registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED,
97                  content::NotificationService::AllSources());
98 
99   NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
100   StartPortalDetection();
101 }
102 
~NetworkPortalDetectorImpl()103 NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() {
104   NET_LOG(EVENT) << "NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl()";
105   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
106 
107   attempt_task_.Cancel();
108   attempt_timeout_.Cancel();
109 
110   captive_portal_detector_->Cancel();
111   captive_portal_detector_.reset();
112   observers_.Clear();
113   if (NetworkHandler::IsInitialized()) {
114     NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
115                                                                    FROM_HERE);
116   }
117   for (auto& observer : observers_)
118     observer.OnShutdown();
119 }
120 
AddObserver(Observer * observer)121 void NetworkPortalDetectorImpl::AddObserver(Observer* observer) {
122   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
123   if (observer && !observers_.HasObserver(observer))
124     observers_.AddObserver(observer);
125 }
126 
AddAndFireObserver(Observer * observer)127 void NetworkPortalDetectorImpl::AddAndFireObserver(Observer* observer) {
128   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
129   if (!observer)
130     return;
131   AddObserver(observer);
132   observer->OnPortalDetectionCompleted(DefaultNetwork(),
133                                        GetCaptivePortalStatus());
134 }
135 
RemoveObserver(Observer * observer)136 void NetworkPortalDetectorImpl::RemoveObserver(Observer* observer) {
137   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
138   if (observer)
139     observers_.RemoveObserver(observer);
140 }
141 
IsEnabled()142 bool NetworkPortalDetectorImpl::IsEnabled() {
143   return enabled_;
144 }
145 
Enable(bool start_detection)146 void NetworkPortalDetectorImpl::Enable(bool start_detection) {
147   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
148   if (enabled_)
149     return;
150 
151   DCHECK(is_idle());
152   enabled_ = true;
153 
154   const NetworkState* network = DefaultNetwork();
155   if (!start_detection || !network)
156     return;
157   NET_LOG(EVENT) << "Starting detection attempt:"
158                  << " id=" << NetworkId(network);
159   SetNetworkPortalDetected(network, false /* portal_detected */);
160   StartDetection();
161 }
162 
163 NetworkPortalDetector::CaptivePortalStatus
GetCaptivePortalStatus()164 NetworkPortalDetectorImpl::GetCaptivePortalStatus() {
165   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
166   return default_portal_status_;
167 }
168 
StartPortalDetection()169 void NetworkPortalDetectorImpl::StartPortalDetection() {
170   if (!is_idle())
171     return;
172   StartDetection();
173   return;
174 }
175 
SetStrategy(PortalDetectorStrategy::StrategyId id)176 void NetworkPortalDetectorImpl::SetStrategy(
177     PortalDetectorStrategy::StrategyId id) {
178   if (id == strategy_->Id())
179     return;
180   strategy_ = PortalDetectorStrategy::CreateById(id, this);
181   if (!is_idle())
182     StopDetection();
183   StartPortalDetection();
184 }
185 
DefaultNetworkChanged(const NetworkState * default_network)186 void NetworkPortalDetectorImpl::DefaultNetworkChanged(
187     const NetworkState* default_network) {
188   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
189 
190   if (!default_network) {
191     NET_LOG(EVENT) << "Default network changed: None";
192 
193     default_network_id_ = std::string();
194     default_proxy_config_ = base::Value();
195 
196     StopDetection();
197 
198     DetectionCompleted(nullptr, CAPTIVE_PORTAL_STATUS_OFFLINE, -1);
199     return;
200   }
201 
202   bool network_changed = (default_network_id_ != default_network->guid());
203   if (network_changed)
204     default_network_id_ = default_network->guid();
205 
206   bool connection_state_changed =
207       (default_connection_state_ != default_network->connection_state());
208   default_connection_state_ = default_network->connection_state();
209 
210   bool proxy_config_changed = false;
211   const base::Value& default_network_proxy_config =
212       default_network->proxy_config();
213   if (default_network_proxy_config.is_none()) {
214     if (!default_proxy_config_.is_none()) {
215       proxy_config_changed = true;
216       default_proxy_config_ = base::Value();
217     }
218   } else if (network_changed ||
219              default_proxy_config_ != default_network_proxy_config) {
220     proxy_config_changed = true;
221     default_proxy_config_ = default_network_proxy_config.Clone();
222   }
223 
224   NET_LOG(EVENT) << "Default network changed:"
225                  << " id=" << NetworkGuidId(default_network_id_)
226                  << " state=" << default_connection_state_
227                  << " changed=" << network_changed
228                  << " proxy_config_changed=" << proxy_config_changed
229                  << " state_changed=" << connection_state_changed;
230 
231   if (network_changed || connection_state_changed || proxy_config_changed)
232     StopDetection();
233 
234   if (!NetworkState::StateIsConnected(default_connection_state_))
235     return;
236 
237   if (proxy_config_changed) {
238     ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec));
239     return;
240   }
241 
242   if (is_idle()) {
243     // Initiate Captive Portal detection if network's captive
244     // portal state is unknown (e.g. for freshly created networks),
245     // offline or if network connection state was changed.
246     CaptivePortalStatus status = GetCaptivePortalStatus();
247     if (status == CAPTIVE_PORTAL_STATUS_UNKNOWN ||
248         status == CAPTIVE_PORTAL_STATUS_OFFLINE ||
249         (!network_changed && connection_state_changed)) {
250       ScheduleAttempt(base::TimeDelta());
251     }
252   }
253 }
254 
NoResponseResultCount()255 int NetworkPortalDetectorImpl::NoResponseResultCount() {
256   return no_response_result_count_;
257 }
258 
AttemptStartTime()259 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() {
260   return attempt_start_time_;
261 }
262 
NowTicks() const263 base::TimeTicks NetworkPortalDetectorImpl::NowTicks() const {
264   if (time_ticks_for_testing_.is_null())
265     return base::TimeTicks::Now();
266   return time_ticks_for_testing_;
267 }
268 
269 ////////////////////////////////////////////////////////////////////////////////
270 // NetworkPortalDetectorImpl, private:
271 
StartDetection()272 void NetworkPortalDetectorImpl::StartDetection() {
273   DCHECK(is_idle());
274 
275   ResetStrategyAndCounters();
276   default_portal_status_ = CAPTIVE_PORTAL_STATUS_UNKNOWN;
277   detection_start_time_ = NowTicks();
278   ScheduleAttempt(base::TimeDelta());
279 }
280 
StopDetection()281 void NetworkPortalDetectorImpl::StopDetection() {
282   attempt_task_.Cancel();
283   attempt_timeout_.Cancel();
284   captive_portal_detector_->Cancel();
285   default_portal_status_ = CAPTIVE_PORTAL_STATUS_UNKNOWN;
286   state_ = STATE_IDLE;
287   ResetStrategyAndCounters();
288 }
289 
ScheduleAttempt(const base::TimeDelta & delay)290 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) {
291   DCHECK(is_idle());
292 
293   if (!IsEnabled())
294     return;
295 
296   attempt_task_.Cancel();
297   attempt_timeout_.Cancel();
298   state_ = STATE_PORTAL_CHECK_PENDING;
299 
300   next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt());
301   attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt,
302                                  weak_factory_.GetWeakPtr()));
303   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
304       FROM_HERE, attempt_task_.callback(), next_attempt_delay_);
305 }
306 
StartAttempt()307 void NetworkPortalDetectorImpl::StartAttempt() {
308   DCHECK(is_portal_check_pending());
309 
310   state_ = STATE_CHECKING_FOR_PORTAL;
311   attempt_start_time_ = NowTicks();
312 
313   NET_LOG(EVENT) << "Starting captive portal detection.";
314   captive_portal_detector_->DetectCaptivePortal(
315       GURL(CaptivePortalDetector::kDefaultURL),
316       base::BindOnce(&NetworkPortalDetectorImpl::OnAttemptCompleted,
317                      weak_factory_.GetWeakPtr()),
318       NO_TRAFFIC_ANNOTATION_YET);
319   attempt_timeout_.Reset(
320       base::Bind(&NetworkPortalDetectorImpl::OnAttemptTimeout,
321                  weak_factory_.GetWeakPtr()));
322 
323   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
324       FROM_HERE, attempt_timeout_.callback(),
325       strategy_->GetNextAttemptTimeout());
326 }
327 
OnAttemptTimeout()328 void NetworkPortalDetectorImpl::OnAttemptTimeout() {
329   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
330   DCHECK(is_checking_for_portal());
331 
332   NET_LOG(EVENT) << "Portal detection timeout: "
333                  << " id=" << NetworkGuidId(default_network_id_);
334 
335   captive_portal_detector_->Cancel();
336   CaptivePortalDetector::Results results;
337   results.result = captive_portal::RESULT_NO_RESPONSE;
338   OnAttemptCompleted(results);
339 }
340 
OnAttemptCompleted(const CaptivePortalDetector::Results & results)341 void NetworkPortalDetectorImpl::OnAttemptCompleted(
342     const CaptivePortalDetector::Results& results) {
343   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
344   DCHECK(is_checking_for_portal());
345 
346   captive_portal::CaptivePortalResult result = results.result;
347   int response_code = results.response_code;
348 
349   const NetworkState* network = DefaultNetwork();
350 
351   // If using a fake profile client, also fake being behind a captive portal
352   // if the default network is in portal state.
353   if (result != captive_portal::RESULT_NO_RESPONSE &&
354       DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() &&
355       network && network->IsShillCaptivePortal()) {
356     result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
357     response_code = 200;
358   }
359 
360   state_ = STATE_IDLE;
361   attempt_timeout_.Cancel();
362 
363   CaptivePortalStatus status = CAPTIVE_PORTAL_STATUS_UNKNOWN;
364   bool no_response_since_portal = false;
365   switch (result) {
366     case captive_portal::RESULT_NO_RESPONSE:
367       if (response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
368         status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED;
369       } else if (network && network->IsShillCaptivePortal()) {
370         // Take into account shill's detection results.
371         status = CAPTIVE_PORTAL_STATUS_PORTAL;
372         no_response_since_portal = true;
373       } else {
374         status = CAPTIVE_PORTAL_STATUS_OFFLINE;
375       }
376       break;
377     case captive_portal::RESULT_INTERNET_CONNECTED:
378       status = CAPTIVE_PORTAL_STATUS_ONLINE;
379       break;
380     case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL:
381       status = CAPTIVE_PORTAL_STATUS_PORTAL;
382       break;
383     default:
384       break;
385   }
386 
387   NET_LOG(EVENT) << "NetworkPortalDetector completed: id="
388                  << NetworkGuidId(default_network_id_) << ", result="
389                  << captive_portal::CaptivePortalResultToString(result)
390                  << ", status=" << status
391                  << ", response_code=" << response_code;
392 
393   UMA_HISTOGRAM_ENUMERATION("CaptivePortal.NetworkPortalDetectorResult",
394                             status);
395   NetworkState::NetworkTechnologyType type =
396       NetworkState::NetworkTechnologyType::kUnknown;
397   if (status == CAPTIVE_PORTAL_STATUS_PORTAL) {
398     if (network)
399       type = network->GetNetworkTechnologyType();
400     UMA_HISTOGRAM_ENUMERATION("CaptivePortal.NetworkPortalDetectorType", type);
401   }
402 
403   if (last_detection_result_ != status) {
404     last_detection_result_ = status;
405     same_detection_result_count_ = 1;
406     net::BackoffEntry::Policy policy = strategy_->policy();
407     if (status == CAPTIVE_PORTAL_STATUS_ONLINE) {
408       policy.initial_delay_ms = kLongInitialDelayBetweenAttemptsMs;
409       policy.maximum_backoff_ms = kLongMaximumDelayBetweenAttemptsMs;
410     } else {
411       policy.initial_delay_ms = kShortInitialDelayBetweenAttemptsMs;
412       policy.maximum_backoff_ms = kShortMaximumDelayBetweenAttemptsMs;
413     }
414     strategy_->SetPolicyAndReset(policy);
415   } else {
416     ++same_detection_result_count_;
417   }
418   strategy_->OnDetectionCompleted();
419 
420   if (result == captive_portal::RESULT_NO_RESPONSE)
421     ++no_response_result_count_;
422   else
423     no_response_result_count_ = 0;
424 
425   if (status != CAPTIVE_PORTAL_STATUS_OFFLINE ||
426       same_detection_result_count_ >= kMaxOfflineResultsBeforeReport) {
427     DetectionCompleted(network, status, response_code);
428   }
429 
430   // Observers (via DetectionCompleted) may already schedule new attempt.
431   if (is_idle())
432     ScheduleAttempt(results.retry_after_delta);
433 }
434 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)435 void NetworkPortalDetectorImpl::Observe(
436     int type,
437     const content::NotificationSource& source,
438     const content::NotificationDetails& details) {
439   if (type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
440       type == chrome::NOTIFICATION_AUTH_CANCELLED) {
441     NET_LOG(EVENT) << "Restarting portal detection due to auth change"
442                    << " id=" << NetworkGuidId(default_network_id_);
443     StopDetection();
444     ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec));
445   }
446 }
447 
DetectionCompleted(const NetworkState * network,const CaptivePortalStatus & status,int response_code)448 void NetworkPortalDetectorImpl::DetectionCompleted(
449     const NetworkState* network,
450     const CaptivePortalStatus& status,
451     int response_code) {
452   default_portal_status_ = status;
453   response_code_for_testing_ = response_code;
454   if (network) {
455     SetNetworkPortalDetected(
456         network, status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL);
457   }
458   for (auto& observer : observers_)
459     observer.OnPortalDetectionCompleted(network, status);
460 }
461 
AttemptTimeoutIsCancelledForTesting() const462 bool NetworkPortalDetectorImpl::AttemptTimeoutIsCancelledForTesting() const {
463   return attempt_timeout_.IsCancelled();
464 }
465 
ResetStrategyAndCounters()466 void NetworkPortalDetectorImpl::ResetStrategyAndCounters() {
467   last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN;
468   same_detection_result_count_ = 0;
469   no_response_result_count_ = 0;
470   strategy_->Reset();
471 }
472 
473 }  // namespace chromeos
474