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.
5 #include "chrome/browser/local_discovery/service_discovery_client_mdns.h"
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"
14 namespace local_discovery
{
16 using content::BrowserThread
;
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
{
22 typedef base::WeakPtr
<Proxy
> WeakPtr
;
24 explicit Proxy(ServiceDiscoveryClientMdns
* client
)
26 weak_ptr_factory_(this) {
27 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
28 client_
->proxies_
.AddObserver(this);
32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
33 client_
->proxies_
.RemoveObserver(this);
36 // Returns true if object is not yet shutdown.
37 virtual bool IsValid() = 0;
39 // Notifies proxies that mDNS layer is going to be destroyed.
40 virtual void OnMdnsDestroy() = 0;
42 // Notifies proxies that new mDNS instance is ready.
43 virtual void OnNewMdnsReady() {
44 DCHECK(!client_
->need_dalay_mdns_tasks_
);
46 for (size_t i
= 0; i
< delayed_tasks_
.size(); ++i
)
47 client_
->mdns_runner_
->PostTask(FROM_HERE
, delayed_tasks_
[i
]);
49 delayed_tasks_
.clear();
52 // Runs callback using this method to abort callback if instance of |Proxy|
54 void RunCallback(const base::Closure
& callback
) {
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
60 void PostToMdnsThread(const base::Closure
& task
) {
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
);
70 delayed_tasks_
.push_back(task
);
73 static bool PostToUIThread(const base::Closure
& task
) {
74 return BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, task
);
77 ServiceDiscoveryClient
* client() {
78 return client_
->client_
.get();
81 WeakPtr
GetWeakPtr() {
82 return weak_ptr_factory_
.GetWeakPtr();
86 void DeleteOnMdnsThread(T
* t
) {
89 if (!client_
->mdns_runner_
->DeleteSoon(FROM_HERE
, t
))
94 scoped_refptr
<ServiceDiscoveryClientMdns
> client_
;
95 // Delayed |mdns_runner_| tasks.
96 std::vector
<base::Closure
> delayed_tasks_
;
97 base::WeakPtrFactory
<Proxy
> weak_ptr_factory_
;
98 DISALLOW_COPY_AND_ASSIGN(Proxy
);
103 const int kMaxRestartAttempts
= 10;
104 const int kRestartDelayOnNetworkChangeSeconds
= 3;
106 typedef base::Callback
<void(bool)> MdnsInitCallback
;
108 class SocketFactory
: public net::MDnsSocketFactory
{
110 explicit SocketFactory(const net::InterfaceIndexFamilyList
& interfaces
)
111 : interfaces_(interfaces
) {}
113 // net::MDnsSocketFactory implementation:
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
));
122 sockets
->push_back(socket
.release());
127 net::InterfaceIndexFamilyList interfaces_
;
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
)));
140 class ProxyBase
: public ServiceDiscoveryClientMdns::Proxy
, public T
{
142 typedef ProxyBase
<T
> Base
;
144 explicit ProxyBase(ServiceDiscoveryClientMdns
* client
)
148 virtual ~ProxyBase() {
149 DeleteOnMdnsThread(implementation_
.release());
152 virtual bool IsValid() override
{
153 return !!implementation();
156 virtual void OnMdnsDestroy() override
{
157 DeleteOnMdnsThread(implementation_
.release());
161 void set_implementation(scoped_ptr
<T
> implementation
) {
162 implementation_
= implementation
.Pass();
165 T
* implementation() const {
166 return implementation_
.get();
170 scoped_ptr
<T
> implementation_
;
171 DISALLOW_COPY_AND_ASSIGN(ProxyBase
);
174 class ServiceWatcherProxy
: public ProxyBase
<ServiceWatcher
> {
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(
186 base::Bind(&ServiceWatcherProxy::OnCallback
, GetWeakPtr(), callback
)));
189 // ServiceWatcher methods.
190 void Start() override
{
191 if (implementation()) {
192 PostToMdnsThread(base::Bind(&ServiceWatcher::Start
,
193 base::Unretained(implementation())));
197 void DiscoverNewServices(bool force_update
) override
{
198 if (implementation()) {
199 PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices
,
200 base::Unretained(implementation()),
205 void SetActivelyRefreshServices(bool actively_refresh_services
) override
{
206 if (implementation()) {
207 PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices
,
208 base::Unretained(implementation()),
209 actively_refresh_services
));
213 std::string
GetServiceType() const override
{ return service_type_
; }
215 void OnNewMdnsReady() override
{
216 ProxyBase
<ServiceWatcher
>::OnNewMdnsReady();
217 if (!implementation())
218 callback_
.Run(ServiceWatcher::UPDATE_INVALIDATED
, "");
222 static void OnCallback(const WeakPtr
& proxy
,
223 const ServiceWatcher::UpdatedCallback
& callback
,
225 const std::string
& a2
) {
226 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
227 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
228 base::Bind(callback
, a1
, a2
)));
230 std::string service_type_
;
231 ServiceWatcher::UpdatedCallback callback_
;
232 DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy
);
235 class ServiceResolverProxy
: public ProxyBase
<ServiceResolver
> {
237 ServiceResolverProxy(ServiceDiscoveryClientMdns
* client_mdns
,
238 const std::string
& service_name
,
239 const ServiceResolver::ResolveCompleteCallback
& callback
)
240 : ProxyBase(client_mdns
),
241 service_name_(service_name
) {
242 // It's safe to call |CreateServiceResolver| on UI thread, because
243 // |MDnsClient| is not used there. It's simplify implementation.
244 set_implementation(client()->CreateServiceResolver(
246 base::Bind(&ServiceResolverProxy::OnCallback
, GetWeakPtr(), callback
)));
249 // ServiceResolver methods.
250 void StartResolving() override
{
251 if (implementation()) {
252 PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving
,
253 base::Unretained(implementation())));
257 std::string
GetName() const override
{ return service_name_
; }
260 static void OnCallback(
261 const WeakPtr
& proxy
,
262 const ServiceResolver::ResolveCompleteCallback
& callback
,
264 const ServiceDescription
& a2
) {
265 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
266 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
267 base::Bind(callback
, a1
, a2
)));
270 std::string service_name_
;
271 DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy
);
274 class LocalDomainResolverProxy
: public ProxyBase
<LocalDomainResolver
> {
276 LocalDomainResolverProxy(
277 ServiceDiscoveryClientMdns
* client_mdns
,
278 const std::string
& domain
,
279 net::AddressFamily address_family
,
280 const LocalDomainResolver::IPAddressCallback
& callback
)
281 : ProxyBase(client_mdns
) {
282 // It's safe to call |CreateLocalDomainResolver| on UI thread, because
283 // |MDnsClient| is not used there. It's simplify implementation.
284 set_implementation(client()->CreateLocalDomainResolver(
288 &LocalDomainResolverProxy::OnCallback
, GetWeakPtr(), callback
)));
291 // LocalDomainResolver methods.
292 void Start() override
{
293 if (implementation()) {
294 PostToMdnsThread(base::Bind(&LocalDomainResolver::Start
,
295 base::Unretained(implementation())));
300 static void OnCallback(const WeakPtr
& proxy
,
301 const LocalDomainResolver::IPAddressCallback
& callback
,
303 const net::IPAddressNumber
& a2
,
304 const net::IPAddressNumber
& a3
) {
305 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
306 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
307 base::Bind(callback
, a1
, a2
, a3
)));
310 DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy
);
315 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
317 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
)),
318 restart_attempts_(0),
319 need_dalay_mdns_tasks_(true),
320 weak_ptr_factory_(this) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
322 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
326 scoped_ptr
<ServiceWatcher
> ServiceDiscoveryClientMdns::CreateServiceWatcher(
327 const std::string
& service_type
,
328 const ServiceWatcher::UpdatedCallback
& callback
) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
330 return scoped_ptr
<ServiceWatcher
>(
331 new ServiceWatcherProxy(this, service_type
, callback
));
334 scoped_ptr
<ServiceResolver
> ServiceDiscoveryClientMdns::CreateServiceResolver(
335 const std::string
& service_name
,
336 const ServiceResolver::ResolveCompleteCallback
& callback
) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
338 return scoped_ptr
<ServiceResolver
>(
339 new ServiceResolverProxy(this, service_name
, callback
));
342 scoped_ptr
<LocalDomainResolver
>
343 ServiceDiscoveryClientMdns::CreateLocalDomainResolver(
344 const std::string
& domain
,
345 net::AddressFamily address_family
,
346 const LocalDomainResolver::IPAddressCallback
& callback
) {
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
348 return scoped_ptr
<LocalDomainResolver
>(
349 new LocalDomainResolverProxy(this, domain
, address_family
, callback
));
352 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
354 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
358 void ServiceDiscoveryClientMdns::OnNetworkChanged(
359 net::NetworkChangeNotifier::ConnectionType type
) {
360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
361 // Only network changes resets counter.
362 restart_attempts_
= 0;
363 ScheduleStartNewClient();
366 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() {
367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
368 OnBeforeMdnsDestroy();
369 if (restart_attempts_
< kMaxRestartAttempts
) {
370 base::MessageLoop::current()->PostDelayedTask(
372 base::Bind(&ServiceDiscoveryClientMdns::StartNewClient
,
373 weak_ptr_factory_
.GetWeakPtr()),
374 base::TimeDelta::FromSeconds(
375 kRestartDelayOnNetworkChangeSeconds
* (1 << restart_attempts_
)));
381 void ServiceDiscoveryClientMdns::StartNewClient() {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
385 mdns_
.reset(net::MDnsClient::CreateDefault().release());
386 client_
.reset(new ServiceDiscoveryClientImpl(mdns_
.get()));
387 BrowserThread::PostTaskAndReplyWithResult(
390 base::Bind(&net::GetMDnsInterfacesToBind
),
391 base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady
,
392 weak_ptr_factory_
.GetWeakPtr()));
395 void ServiceDiscoveryClientMdns::OnInterfaceListReady(
396 const net::InterfaceIndexFamilyList
& interfaces
) {
397 mdns_runner_
->PostTask(
399 base::Bind(&InitMdns
,
400 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized
,
401 weak_ptr_factory_
.GetWeakPtr()),
403 base::Unretained(mdns_
.get())));
406 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success
) {
407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
409 ScheduleStartNewClient();
414 // Initialization is done, no need to delay tasks.
415 need_dalay_mdns_tasks_
= false;
416 FOR_EACH_OBSERVER(Proxy
, proxies_
, OnNewMdnsReady());
419 void ServiceDiscoveryClientMdns::ReportSuccess() {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
421 UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
425 void ServiceDiscoveryClientMdns::OnBeforeMdnsDestroy() {
426 need_dalay_mdns_tasks_
= true;
427 weak_ptr_factory_
.InvalidateWeakPtrs();
428 FOR_EACH_OBSERVER(Proxy
, proxies_
, OnMdnsDestroy());
431 void ServiceDiscoveryClientMdns::DestroyMdns() {
432 OnBeforeMdnsDestroy();
433 // After calling |Proxy::OnMdnsDestroy| all references to client_ and mdns_
434 // should be destroyed.
436 mdns_runner_
->DeleteSoon(FROM_HERE
, client_
.release());
438 mdns_runner_
->DeleteSoon(FROM_HERE
, mdns_
.release());
441 } // namespace local_discovery