1 // Copyright 2014 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/local_discovery/service_discovery_client_mdns.h" 6 7 #include "base/memory/scoped_vector.h" 8 #include "base/metrics/histogram.h" 9 #include "chrome/common/local_discovery/service_discovery_client_impl.h" 10 #include "content/public/browser/browser_thread.h" 11 #include "net/dns/mdns_client.h" 12 #include "net/udp/datagram_server_socket.h" 13 14 namespace local_discovery { 15 16 using content::BrowserThread; 17 18 // Base class for objects returned by ServiceDiscoveryClient implementation. 19 // Handles interaction of client code on UI thread end net code on mdns thread. 20 class ServiceDiscoveryClientMdns::Proxy { 21 public: 22 typedef base::WeakPtr<Proxy> WeakPtr; 23 24 explicit Proxy(ServiceDiscoveryClientMdns* client) 25 : client_(client), 26 weak_ptr_factory_(this) { 27 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 28 client_->proxies_.AddObserver(this); 29 } 30 31 virtual ~Proxy() { 32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 33 client_->proxies_.RemoveObserver(this); 34 } 35 36 // Returns true if object is not yet shutdown. 37 virtual bool IsValid() = 0; 38 39 // Notifies proxies that mDNS layer is going to be destroyed. 40 virtual void OnMdnsDestroy() = 0; 41 42 // Notifies proxies that new mDNS instance is ready. 43 virtual void OnNewMdnsReady() { 44 DCHECK(!client_->need_dalay_mdns_tasks_); 45 if (IsValid()) { 46 for (size_t i = 0; i < delayed_tasks_.size(); ++i) 47 client_->mdns_runner_->PostTask(FROM_HERE, delayed_tasks_[i]); 48 } 49 delayed_tasks_.clear(); 50 } 51 52 // Runs callback using this method to abort callback if instance of |Proxy| 53 // is deleted. 54 void RunCallback(const base::Closure& callback) { 55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 56 callback.Run(); 57 } 58 59 protected: 60 void PostToMdnsThread(const base::Closure& task) { 61 DCHECK(IsValid()); 62 // The first task on IO thread for each |mdns_| instance must be |InitMdns|. 63 // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind| 64 // running on FILE thread, so |PostToMdnsThread| could be called to post 65 // task for |mdns_| that is not initialized yet. 66 if (!client_->need_dalay_mdns_tasks_) { 67 client_->mdns_runner_->PostTask(FROM_HERE, task); 68 return; 69 } 70 delayed_tasks_.push_back(task); 71 } 72 73 static bool PostToUIThread(const base::Closure& task) { 74 return BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task); 75 } 76 77 ServiceDiscoveryClient* client() { 78 return client_->client_.get(); 79 } 80 81 WeakPtr GetWeakPtr() { 82 return weak_ptr_factory_.GetWeakPtr(); 83 } 84 85 template<class T> 86 void DeleteOnMdnsThread(T* t) { 87 if (!t) 88 return; 89 if (!client_->mdns_runner_->DeleteSoon(FROM_HERE, t)) 90 delete t; 91 } 92 93 private: 94 scoped_refptr<ServiceDiscoveryClientMdns> client_; 95 base::WeakPtrFactory<Proxy> weak_ptr_factory_; 96 // Delayed |mdns_runner_| tasks. 97 std::vector<base::Closure> delayed_tasks_; 98 DISALLOW_COPY_AND_ASSIGN(Proxy); 99 }; 100 101 namespace { 102 103 const int kMaxRestartAttempts = 10; 104 const int kRestartDelayOnNetworkChangeSeconds = 3; 105 106 typedef base::Callback<void(bool)> MdnsInitCallback; 107 108 class SocketFactory : public net::MDnsSocketFactory { 109 public: 110 explicit SocketFactory(const net::InterfaceIndexFamilyList& interfaces) 111 : interfaces_(interfaces) {} 112 113 // net::MDnsSocketFactory implementation: 114 virtual void CreateSockets( 115 ScopedVector<net::DatagramServerSocket>* sockets) OVERRIDE { 116 for (size_t i = 0; i < interfaces_.size(); ++i) { 117 DCHECK(interfaces_[i].second == net::ADDRESS_FAMILY_IPV4 || 118 interfaces_[i].second == net::ADDRESS_FAMILY_IPV6); 119 scoped_ptr<net::DatagramServerSocket> socket( 120 CreateAndBindMDnsSocket(interfaces_[i].second, interfaces_[i].first)); 121 if (socket) 122 sockets->push_back(socket.release()); 123 } 124 } 125 126 private: 127 net::InterfaceIndexFamilyList interfaces_; 128 }; 129 130 void InitMdns(const MdnsInitCallback& on_initialized, 131 const net::InterfaceIndexFamilyList& interfaces, 132 net::MDnsClient* mdns) { 133 SocketFactory socket_factory(interfaces); 134 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 135 base::Bind(on_initialized, 136 mdns->StartListening(&socket_factory))); 137 } 138 139 template<class T> 140 class ProxyBase : public ServiceDiscoveryClientMdns::Proxy, public T { 141 public: 142 typedef ProxyBase<T> Base; 143 144 explicit ProxyBase(ServiceDiscoveryClientMdns* client) 145 : Proxy(client) { 146 } 147 148 virtual ~ProxyBase() { 149 DeleteOnMdnsThread(implementation_.release()); 150 } 151 152 virtual bool IsValid() OVERRIDE { 153 return !!implementation(); 154 } 155 156 virtual void OnMdnsDestroy() OVERRIDE { 157 DeleteOnMdnsThread(implementation_.release()); 158 }; 159 160 protected: 161 void set_implementation(scoped_ptr<T> implementation) { 162 implementation_ = implementation.Pass(); 163 } 164 165 T* implementation() const { 166 return implementation_.get(); 167 } 168 169 private: 170 scoped_ptr<T> implementation_; 171 DISALLOW_COPY_AND_ASSIGN(ProxyBase); 172 }; 173 174 class ServiceWatcherProxy : public ProxyBase<ServiceWatcher> { 175 public: 176 ServiceWatcherProxy(ServiceDiscoveryClientMdns* client_mdns, 177 const std::string& service_type, 178 const ServiceWatcher::UpdatedCallback& callback) 179 : ProxyBase(client_mdns), 180 service_type_(service_type), 181 callback_(callback) { 182 // It's safe to call |CreateServiceWatcher| on UI thread, because 183 // |MDnsClient| is not used there. It's simplify implementation. 184 set_implementation(client()->CreateServiceWatcher( 185 service_type, 186 base::Bind(&ServiceWatcherProxy::OnCallback, GetWeakPtr(), callback))); 187 } 188 189 // ServiceWatcher methods. 190 virtual void Start() OVERRIDE { 191 if (implementation()) { 192 PostToMdnsThread(base::Bind(&ServiceWatcher::Start, 193 base::Unretained(implementation()))); 194 } 195 } 196 197 virtual void DiscoverNewServices(bool force_update) OVERRIDE { 198 if (implementation()) { 199 PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices, 200 base::Unretained(implementation()), 201 force_update)); 202 } 203 } 204 205 virtual void SetActivelyRefreshServices( 206 bool actively_refresh_services) OVERRIDE { 207 if (implementation()) { 208 PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices, 209 base::Unretained(implementation()), 210 actively_refresh_services)); 211 } 212 } 213 214 virtual std::string GetServiceType() const OVERRIDE { 215 return service_type_; 216 } 217 218 virtual void OnNewMdnsReady() OVERRIDE { 219 ProxyBase<ServiceWatcher>::OnNewMdnsReady(); 220 if (!implementation()) 221 callback_.Run(ServiceWatcher::UPDATE_INVALIDATED, ""); 222 } 223 224 private: 225 static void OnCallback(const WeakPtr& proxy, 226 const ServiceWatcher::UpdatedCallback& callback, 227 UpdateType a1, 228 const std::string& a2) { 229 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 230 PostToUIThread(base::Bind(&Base::RunCallback, proxy, 231 base::Bind(callback, a1, a2))); 232 } 233 std::string service_type_; 234 ServiceWatcher::UpdatedCallback callback_; 235 DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy); 236 }; 237 238 class ServiceResolverProxy : public ProxyBase<ServiceResolver> { 239 public: 240 ServiceResolverProxy(ServiceDiscoveryClientMdns* client_mdns, 241 const std::string& service_name, 242 const ServiceResolver::ResolveCompleteCallback& callback) 243 : ProxyBase(client_mdns), 244 service_name_(service_name) { 245 // It's safe to call |CreateServiceResolver| on UI thread, because 246 // |MDnsClient| is not used there. It's simplify implementation. 247 set_implementation(client()->CreateServiceResolver( 248 service_name, 249 base::Bind(&ServiceResolverProxy::OnCallback, GetWeakPtr(), callback))); 250 } 251 252 // ServiceResolver methods. 253 virtual void StartResolving() OVERRIDE { 254 if (implementation()) { 255 PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving, 256 base::Unretained(implementation()))); 257 } 258 }; 259 260 virtual std::string GetName() const OVERRIDE { 261 return service_name_; 262 } 263 264 private: 265 static void OnCallback( 266 const WeakPtr& proxy, 267 const ServiceResolver::ResolveCompleteCallback& callback, 268 RequestStatus a1, 269 const ServiceDescription& a2) { 270 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 271 PostToUIThread(base::Bind(&Base::RunCallback, proxy, 272 base::Bind(callback, a1, a2))); 273 } 274 275 std::string service_name_; 276 DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy); 277 }; 278 279 class LocalDomainResolverProxy : public ProxyBase<LocalDomainResolver> { 280 public: 281 LocalDomainResolverProxy( 282 ServiceDiscoveryClientMdns* client_mdns, 283 const std::string& domain, 284 net::AddressFamily address_family, 285 const LocalDomainResolver::IPAddressCallback& callback) 286 : ProxyBase(client_mdns) { 287 // It's safe to call |CreateLocalDomainResolver| on UI thread, because 288 // |MDnsClient| is not used there. It's simplify implementation. 289 set_implementation(client()->CreateLocalDomainResolver( 290 domain, 291 address_family, 292 base::Bind( 293 &LocalDomainResolverProxy::OnCallback, GetWeakPtr(), callback))); 294 } 295 296 // LocalDomainResolver methods. 297 virtual void Start() OVERRIDE { 298 if (implementation()) { 299 PostToMdnsThread(base::Bind(&LocalDomainResolver::Start, 300 base::Unretained(implementation()))); 301 } 302 }; 303 304 private: 305 static void OnCallback(const WeakPtr& proxy, 306 const LocalDomainResolver::IPAddressCallback& callback, 307 bool a1, 308 const net::IPAddressNumber& a2, 309 const net::IPAddressNumber& a3) { 310 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 311 PostToUIThread(base::Bind(&Base::RunCallback, proxy, 312 base::Bind(callback, a1, a2, a3))); 313 } 314 315 DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy); 316 }; 317 318 } // namespace 319 320 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns() 321 : mdns_runner_( 322 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)), 323 restart_attempts_(0), 324 need_dalay_mdns_tasks_(true), 325 weak_ptr_factory_(this) { 326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 327 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); 328 StartNewClient(); 329 } 330 331 scoped_ptr<ServiceWatcher> ServiceDiscoveryClientMdns::CreateServiceWatcher( 332 const std::string& service_type, 333 const ServiceWatcher::UpdatedCallback& callback) { 334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 335 return scoped_ptr<ServiceWatcher>( 336 new ServiceWatcherProxy(this, service_type, callback)); 337 } 338 339 scoped_ptr<ServiceResolver> ServiceDiscoveryClientMdns::CreateServiceResolver( 340 const std::string& service_name, 341 const ServiceResolver::ResolveCompleteCallback& callback) { 342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 343 return scoped_ptr<ServiceResolver>( 344 new ServiceResolverProxy(this, service_name, callback)); 345 } 346 347 scoped_ptr<LocalDomainResolver> 348 ServiceDiscoveryClientMdns::CreateLocalDomainResolver( 349 const std::string& domain, 350 net::AddressFamily address_family, 351 const LocalDomainResolver::IPAddressCallback& callback) { 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 353 return scoped_ptr<LocalDomainResolver>( 354 new LocalDomainResolverProxy(this, domain, address_family, callback)); 355 } 356 357 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() { 358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 359 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); 360 DestroyMdns(); 361 } 362 363 void ServiceDiscoveryClientMdns::OnNetworkChanged( 364 net::NetworkChangeNotifier::ConnectionType type) { 365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 366 // Only network changes resets counter. 367 restart_attempts_ = 0; 368 ScheduleStartNewClient(); 369 } 370 371 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() { 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 373 OnBeforeMdnsDestroy(); 374 if (restart_attempts_ < kMaxRestartAttempts) { 375 base::MessageLoop::current()->PostDelayedTask( 376 FROM_HERE, 377 base::Bind(&ServiceDiscoveryClientMdns::StartNewClient, 378 weak_ptr_factory_.GetWeakPtr()), 379 base::TimeDelta::FromSeconds( 380 kRestartDelayOnNetworkChangeSeconds * (1 << restart_attempts_))); 381 } else { 382 ReportSuccess(); 383 } 384 } 385 386 void ServiceDiscoveryClientMdns::StartNewClient() { 387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 388 ++restart_attempts_; 389 DestroyMdns(); 390 mdns_.reset(net::MDnsClient::CreateDefault().release()); 391 client_.reset(new ServiceDiscoveryClientImpl(mdns_.get())); 392 BrowserThread::PostTaskAndReplyWithResult( 393 BrowserThread::FILE, 394 FROM_HERE, 395 base::Bind(&net::GetMDnsInterfacesToBind), 396 base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady, 397 weak_ptr_factory_.GetWeakPtr())); 398 } 399 400 void ServiceDiscoveryClientMdns::OnInterfaceListReady( 401 const net::InterfaceIndexFamilyList& interfaces) { 402 mdns_runner_->PostTask( 403 FROM_HERE, 404 base::Bind(&InitMdns, 405 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized, 406 weak_ptr_factory_.GetWeakPtr()), 407 interfaces, 408 base::Unretained(mdns_.get()))); 409 } 410 411 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) { 412 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 413 if (!success) { 414 ScheduleStartNewClient(); 415 return; 416 } 417 ReportSuccess(); 418 419 // Initialization is done, no need to delay tasks. 420 need_dalay_mdns_tasks_ = false; 421 FOR_EACH_OBSERVER(Proxy, proxies_, OnNewMdnsReady()); 422 } 423 424 void ServiceDiscoveryClientMdns::ReportSuccess() { 425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 426 UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts", 427 restart_attempts_); 428 } 429 430 void ServiceDiscoveryClientMdns::OnBeforeMdnsDestroy() { 431 need_dalay_mdns_tasks_ = true; 432 weak_ptr_factory_.InvalidateWeakPtrs(); 433 FOR_EACH_OBSERVER(Proxy, proxies_, OnMdnsDestroy()); 434 } 435 436 void ServiceDiscoveryClientMdns::DestroyMdns() { 437 OnBeforeMdnsDestroy(); 438 // After calling |Proxy::OnMdnsDestroy| all references to client_ and mdns_ 439 // should be destroyed. 440 if (client_) 441 mdns_runner_->DeleteSoon(FROM_HERE, client_.release()); 442 if (mdns_) 443 mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release()); 444 } 445 446 } // namespace local_discovery 447