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_
.insert(this);
32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
33 client_
->proxies_
.erase(this);
36 // Notify proxies that mDNS layer is going to be destroyed.
37 virtual void OnMdnsDestroy() = 0;
39 // Notify proxies that new mDNS instance is ready.
40 virtual void OnNewMdnsReady() {}
42 // Run callback using this method to abort callback if instance of |Proxy|
44 void RunCallback(const base::Closure
& callback
) {
45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
50 bool PostToMdnsThread(const base::Closure
& task
) {
51 return client_
->PostToMdnsThread(task
);
54 static bool PostToUIThread(const base::Closure
& task
) {
55 return BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, task
);
58 ServiceDiscoveryClient
* client() {
59 return client_
->client_
.get();
62 WeakPtr
GetWeakPtr() {
63 return weak_ptr_factory_
.GetWeakPtr();
67 void DeleteOnMdnsThread(T
* t
) {
70 if (!client_
->mdns_runner_
->DeleteSoon(FROM_HERE
, t
))
75 scoped_refptr
<ServiceDiscoveryClientMdns
> client_
;
76 base::WeakPtrFactory
<Proxy
> weak_ptr_factory_
;
78 DISALLOW_COPY_AND_ASSIGN(Proxy
);
83 const int kMaxRestartAttempts
= 10;
84 const int kRestartDelayOnNetworkChangeSeconds
= 3;
86 typedef base::Callback
<void(bool)> MdnsInitCallback
;
88 class SocketFactory
: public net::MDnsSocketFactory
{
90 explicit SocketFactory(const net::InterfaceIndexFamilyList
& interfaces
)
91 : interfaces_(interfaces
) {}
93 // net::MDnsSocketFactory implementation:
94 virtual void CreateSockets(
95 ScopedVector
<net::DatagramServerSocket
>* sockets
) OVERRIDE
{
96 for (size_t i
= 0; i
< interfaces_
.size(); ++i
) {
97 DCHECK(interfaces_
[i
].second
== net::ADDRESS_FAMILY_IPV4
||
98 interfaces_
[i
].second
== net::ADDRESS_FAMILY_IPV6
);
99 scoped_ptr
<net::DatagramServerSocket
> socket(
100 CreateAndBindMDnsSocket(interfaces_
[i
].second
, interfaces_
[i
].first
));
102 sockets
->push_back(socket
.release());
107 net::InterfaceIndexFamilyList interfaces_
;
110 void InitMdns(const MdnsInitCallback
& on_initialized
,
111 const net::InterfaceIndexFamilyList
& interfaces
,
112 net::MDnsClient
* mdns
) {
113 SocketFactory
socket_factory(interfaces
);
114 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
115 base::Bind(on_initialized
,
116 mdns
->StartListening(&socket_factory
)));
120 class ProxyBase
: public ServiceDiscoveryClientMdns::Proxy
, public T
{
122 typedef base::WeakPtr
<Proxy
> WeakPtr
;
123 typedef ProxyBase
<T
> Base
;
125 explicit ProxyBase(ServiceDiscoveryClientMdns
* client
)
129 virtual ~ProxyBase() {
130 DeleteOnMdnsThread(implementation_
.release());
133 virtual void OnMdnsDestroy() OVERRIDE
{
134 DeleteOnMdnsThread(implementation_
.release());
138 void set_implementation(scoped_ptr
<T
> implementation
) {
139 implementation_
= implementation
.Pass();
142 T
* implementation() const {
143 return implementation_
.get();
147 scoped_ptr
<T
> implementation_
;
148 DISALLOW_COPY_AND_ASSIGN(ProxyBase
);
151 class ServiceWatcherProxy
: public ProxyBase
<ServiceWatcher
> {
153 ServiceWatcherProxy(ServiceDiscoveryClientMdns
* client_mdns
,
154 const std::string
& service_type
,
155 const ServiceWatcher::UpdatedCallback
& callback
)
156 : ProxyBase(client_mdns
),
157 service_type_(service_type
),
158 callback_(callback
) {
159 // It's safe to call |CreateServiceWatcher| on UI thread, because
160 // |MDnsClient| is not used there. It's simplify implementation.
161 set_implementation(client()->CreateServiceWatcher(
163 base::Bind(&ServiceWatcherProxy::OnCallback
, GetWeakPtr(), callback
)));
166 // ServiceWatcher methods.
167 virtual void Start() OVERRIDE
{
168 if (implementation())
169 PostToMdnsThread(base::Bind(&ServiceWatcher::Start
,
170 base::Unretained(implementation())));
173 virtual void DiscoverNewServices(bool force_update
) OVERRIDE
{
174 if (implementation())
175 PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices
,
176 base::Unretained(implementation()),
180 virtual void SetActivelyRefreshServices(
181 bool actively_refresh_services
) OVERRIDE
{
182 if (implementation())
183 PostToMdnsThread(base::Bind(&ServiceWatcher::SetActivelyRefreshServices
,
184 base::Unretained(implementation()),
185 actively_refresh_services
));
188 virtual std::string
GetServiceType() const OVERRIDE
{
189 return service_type_
;
192 virtual void OnNewMdnsReady() OVERRIDE
{
193 if (!implementation())
194 callback_
.Run(ServiceWatcher::UPDATE_INVALIDATED
, "");
198 static void OnCallback(const WeakPtr
& proxy
,
199 const ServiceWatcher::UpdatedCallback
& callback
,
201 const std::string
& a2
) {
202 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
203 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
204 base::Bind(callback
, a1
, a2
)));
206 std::string service_type_
;
207 ServiceWatcher::UpdatedCallback callback_
;
208 DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy
);
211 class ServiceResolverProxy
: public ProxyBase
<ServiceResolver
> {
213 ServiceResolverProxy(ServiceDiscoveryClientMdns
* client_mdns
,
214 const std::string
& service_name
,
215 const ServiceResolver::ResolveCompleteCallback
& callback
)
216 : ProxyBase(client_mdns
),
217 service_name_(service_name
) {
218 // It's safe to call |CreateServiceResolver| on UI thread, because
219 // |MDnsClient| is not used there. It's simplify implementation.
220 set_implementation(client()->CreateServiceResolver(
222 base::Bind(&ServiceResolverProxy::OnCallback
, GetWeakPtr(), callback
)));
225 // ServiceResolver methods.
226 virtual void StartResolving() OVERRIDE
{
227 if (implementation())
228 PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving
,
229 base::Unretained(implementation())));
232 virtual std::string
GetName() const OVERRIDE
{
233 return service_name_
;
237 static void OnCallback(
238 const WeakPtr
& proxy
,
239 const ServiceResolver::ResolveCompleteCallback
& callback
,
241 const ServiceDescription
& a2
) {
242 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
243 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
244 base::Bind(callback
, a1
, a2
)));
247 std::string service_name_
;
248 DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy
);
251 class LocalDomainResolverProxy
: public ProxyBase
<LocalDomainResolver
> {
253 LocalDomainResolverProxy(
254 ServiceDiscoveryClientMdns
* client_mdns
,
255 const std::string
& domain
,
256 net::AddressFamily address_family
,
257 const LocalDomainResolver::IPAddressCallback
& callback
)
258 : ProxyBase(client_mdns
) {
259 // It's safe to call |CreateLocalDomainResolver| on UI thread, because
260 // |MDnsClient| is not used there. It's simplify implementation.
261 set_implementation(client()->CreateLocalDomainResolver(
265 &LocalDomainResolverProxy::OnCallback
, GetWeakPtr(), callback
)));
268 // LocalDomainResolver methods.
269 virtual void Start() OVERRIDE
{
270 if (implementation())
271 PostToMdnsThread(base::Bind(&LocalDomainResolver::Start
,
272 base::Unretained(implementation())));
276 static void OnCallback(const WeakPtr
& proxy
,
277 const LocalDomainResolver::IPAddressCallback
& callback
,
279 const net::IPAddressNumber
& a2
,
280 const net::IPAddressNumber
& a3
) {
281 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
282 PostToUIThread(base::Bind(&Base::RunCallback
, proxy
,
283 base::Bind(callback
, a1
, a2
, a3
)));
286 DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy
);
291 ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
293 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
)),
294 restart_attempts_(0),
295 need_dalay_mdns_tasks_(true),
296 weak_ptr_factory_(this) {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
298 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
302 scoped_ptr
<ServiceWatcher
> ServiceDiscoveryClientMdns::CreateServiceWatcher(
303 const std::string
& service_type
,
304 const ServiceWatcher::UpdatedCallback
& callback
) {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
306 return scoped_ptr
<ServiceWatcher
>(
307 new ServiceWatcherProxy(this, service_type
, callback
));
310 scoped_ptr
<ServiceResolver
> ServiceDiscoveryClientMdns::CreateServiceResolver(
311 const std::string
& service_name
,
312 const ServiceResolver::ResolveCompleteCallback
& callback
) {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
314 return scoped_ptr
<ServiceResolver
>(
315 new ServiceResolverProxy(this, service_name
, callback
));
318 scoped_ptr
<LocalDomainResolver
>
319 ServiceDiscoveryClientMdns::CreateLocalDomainResolver(
320 const std::string
& domain
,
321 net::AddressFamily address_family
,
322 const LocalDomainResolver::IPAddressCallback
& callback
) {
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
324 return scoped_ptr
<LocalDomainResolver
>(
325 new LocalDomainResolverProxy(this, domain
, address_family
, callback
));
328 ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
330 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
331 DCHECK(proxies_
.empty());
335 void ServiceDiscoveryClientMdns::OnNetworkChanged(
336 net::NetworkChangeNotifier::ConnectionType type
) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
338 // Only network changes resets counter.
339 restart_attempts_
= 0;
340 ScheduleStartNewClient();
343 void ServiceDiscoveryClientMdns::ScheduleStartNewClient() {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
345 // Reset pointer to abort another restart request, if already scheduled.
346 weak_ptr_factory_
.InvalidateWeakPtrs();
347 if (restart_attempts_
< kMaxRestartAttempts
) {
348 base::MessageLoop::current()->PostDelayedTask(
350 base::Bind(&ServiceDiscoveryClientMdns::StartNewClient
,
351 weak_ptr_factory_
.GetWeakPtr()),
352 base::TimeDelta::FromSeconds(
353 kRestartDelayOnNetworkChangeSeconds
* (1 << restart_attempts_
)));
359 void ServiceDiscoveryClientMdns::StartNewClient() {
360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
363 mdns_
.reset(net::MDnsClient::CreateDefault().release());
364 client_
.reset(new ServiceDiscoveryClientImpl(mdns_
.get()));
365 BrowserThread::PostTaskAndReplyWithResult(
368 base::Bind(&net::GetMDnsInterfacesToBind
),
369 base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady
,
370 weak_ptr_factory_
.GetWeakPtr()));
373 void ServiceDiscoveryClientMdns::OnInterfaceListReady(
374 const net::InterfaceIndexFamilyList
& interfaces
) {
375 mdns_runner_
->PostTask(
377 base::Bind(&InitMdns
,
378 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized
,
379 weak_ptr_factory_
.GetWeakPtr()),
381 base::Unretained(mdns_
.get())));
382 // Initialization is posted, no need to delay tasks.
383 need_dalay_mdns_tasks_
= false;
384 for (size_t i
= 0; i
< delayed_tasks_
.size(); ++i
)
385 mdns_runner_
->PostTask(FROM_HERE
, delayed_tasks_
[i
]);
386 delayed_tasks_
.clear();
389 void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success
) {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
392 ScheduleStartNewClient();
397 std::set
<Proxy
*> tmp_proxies(proxies_
);
398 std::for_each(tmp_proxies
.begin(), tmp_proxies
.end(),
399 std::mem_fun(&Proxy::OnNewMdnsReady
));
402 void ServiceDiscoveryClientMdns::ReportSuccess() {
403 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
404 UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
408 void ServiceDiscoveryClientMdns::Reset() {
409 need_dalay_mdns_tasks_
= true;
410 delayed_tasks_
.clear();
412 weak_ptr_factory_
.InvalidateWeakPtrs();
414 std::for_each(proxies_
.begin(), proxies_
.end(),
415 std::mem_fun(&Proxy::OnMdnsDestroy
));
417 mdns_runner_
->DeleteSoon(FROM_HERE
, client_
.release());
419 mdns_runner_
->DeleteSoon(FROM_HERE
, mdns_
.release());
422 bool ServiceDiscoveryClientMdns::PostToMdnsThread(const base::Closure
& task
) {
423 // The first task on IO thread for each |mdns_| instance must be |InitMdns|.
424 // |OnInterfaceListReady| could be delayed by |GetMDnsInterfacesToBind|
425 // running on FILE thread, so |PostToMdnsThread| could to post task for
426 // |mdns_| that is not posted initialization for.
427 if (!need_dalay_mdns_tasks_
)
428 return mdns_runner_
->PostTask(FROM_HERE
, task
);
429 delayed_tasks_
.push_back(task
);
433 } // namespace local_discovery