service_discovery_client_mdns.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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
14namespace local_discovery {
15
16using 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.
20class 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_.insert(this);
29  }
30
31  virtual ~Proxy() {
32    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
33    client_->proxies_.erase(this);
34  }
35
36  // Notify proxies that mDNS layer is going to be destroyed.
37  virtual void OnMdnsDestroy() = 0;
38
39  // Notify proxies that new mDNS instance is ready.
40  virtual void OnNewMdnsReady() {}
41
42  // Run callback using this method to abort callback if instance of |Proxy|
43  // is deleted.
44  void RunCallback(const base::Closure& callback) {
45    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
46    callback.Run();
47  }
48
49 protected:
50  bool PostToMdnsThread(const base::Closure& task) {
51    return client_->PostToMdnsThread(task);
52  }
53
54  static bool PostToUIThread(const base::Closure& task) {
55    return BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
56  }
57
58  ServiceDiscoveryClient* client() {
59    return client_->client_.get();
60  }
61
62  WeakPtr GetWeakPtr() {
63    return weak_ptr_factory_.GetWeakPtr();
64  }
65
66  template<class T>
67  void DeleteOnMdnsThread(T* t) {
68    if (!t)
69      return;
70    if (!client_->mdns_runner_->DeleteSoon(FROM_HERE, t))
71      delete t;
72  }
73
74 private:
75  scoped_refptr<ServiceDiscoveryClientMdns> client_;
76  base::WeakPtrFactory<Proxy> weak_ptr_factory_;
77
78  DISALLOW_COPY_AND_ASSIGN(Proxy);
79};
80
81namespace {
82
83const int kMaxRestartAttempts = 10;
84const int kRestartDelayOnNetworkChangeSeconds = 3;
85
86typedef base::Callback<void(bool)> MdnsInitCallback;
87
88class SocketFactory : public net::MDnsSocketFactory {
89 public:
90  explicit SocketFactory(const net::InterfaceIndexFamilyList& interfaces)
91      : interfaces_(interfaces) {}
92
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));
101      if (socket)
102        sockets->push_back(socket.release());
103    }
104  }
105
106 private:
107  net::InterfaceIndexFamilyList interfaces_;
108};
109
110void 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)));
117}
118
119template<class T>
120class ProxyBase : public ServiceDiscoveryClientMdns::Proxy, public T {
121 public:
122  typedef base::WeakPtr<Proxy> WeakPtr;
123  typedef ProxyBase<T> Base;
124
125  explicit ProxyBase(ServiceDiscoveryClientMdns* client)
126      : Proxy(client) {
127  }
128
129  virtual ~ProxyBase() {
130    DeleteOnMdnsThread(implementation_.release());
131  }
132
133  virtual void OnMdnsDestroy() OVERRIDE {
134    DeleteOnMdnsThread(implementation_.release());
135  };
136
137 protected:
138  void set_implementation(scoped_ptr<T> implementation) {
139    implementation_ = implementation.Pass();
140  }
141
142  T* implementation()  const {
143    return implementation_.get();
144  }
145
146 private:
147  scoped_ptr<T> implementation_;
148  DISALLOW_COPY_AND_ASSIGN(ProxyBase);
149};
150
151class ServiceWatcherProxy : public ProxyBase<ServiceWatcher> {
152 public:
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(
162        service_type,
163        base::Bind(&ServiceWatcherProxy::OnCallback, GetWeakPtr(), callback)));
164  }
165
166  // ServiceWatcher methods.
167  virtual void Start() OVERRIDE {
168    if (implementation())
169      PostToMdnsThread(base::Bind(&ServiceWatcher::Start,
170                                  base::Unretained(implementation())));
171  }
172
173  virtual void DiscoverNewServices(bool force_update) OVERRIDE {
174    if (implementation())
175      PostToMdnsThread(base::Bind(&ServiceWatcher::DiscoverNewServices,
176                                  base::Unretained(implementation()),
177                                  force_update));
178  }
179
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));
186  }
187
188  virtual std::string GetServiceType() const OVERRIDE {
189    return service_type_;
190  }
191
192  virtual void OnNewMdnsReady() OVERRIDE {
193    if (!implementation())
194      callback_.Run(ServiceWatcher::UPDATE_INVALIDATED, "");
195  }
196
197 private:
198  static void OnCallback(const WeakPtr& proxy,
199                         const ServiceWatcher::UpdatedCallback& callback,
200                         UpdateType a1,
201                         const std::string& a2) {
202    DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
203    PostToUIThread(base::Bind(&Base::RunCallback, proxy,
204                              base::Bind(callback, a1, a2)));
205  }
206  std::string service_type_;
207  ServiceWatcher::UpdatedCallback callback_;
208  DISALLOW_COPY_AND_ASSIGN(ServiceWatcherProxy);
209};
210
211class ServiceResolverProxy : public ProxyBase<ServiceResolver> {
212 public:
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(
221        service_name,
222        base::Bind(&ServiceResolverProxy::OnCallback, GetWeakPtr(), callback)));
223  }
224
225  // ServiceResolver methods.
226  virtual void StartResolving() OVERRIDE {
227    if (implementation())
228      PostToMdnsThread(base::Bind(&ServiceResolver::StartResolving,
229                                  base::Unretained(implementation())));
230  };
231
232  virtual std::string GetName() const OVERRIDE {
233    return service_name_;
234  }
235
236 private:
237  static void OnCallback(
238      const WeakPtr& proxy,
239      const ServiceResolver::ResolveCompleteCallback& callback,
240      RequestStatus a1,
241      const ServiceDescription& a2) {
242    DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
243    PostToUIThread(base::Bind(&Base::RunCallback, proxy,
244                              base::Bind(callback, a1, a2)));
245  }
246
247  std::string service_name_;
248  DISALLOW_COPY_AND_ASSIGN(ServiceResolverProxy);
249};
250
251class LocalDomainResolverProxy : public ProxyBase<LocalDomainResolver> {
252 public:
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(
262        domain,
263        address_family,
264        base::Bind(
265            &LocalDomainResolverProxy::OnCallback, GetWeakPtr(), callback)));
266  }
267
268  // LocalDomainResolver methods.
269  virtual void Start() OVERRIDE {
270    if (implementation())
271      PostToMdnsThread(base::Bind(&LocalDomainResolver::Start,
272                                  base::Unretained(implementation())));
273  };
274
275 private:
276  static void OnCallback(const WeakPtr& proxy,
277                         const LocalDomainResolver::IPAddressCallback& callback,
278                         bool a1,
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)));
284  }
285
286  DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverProxy);
287};
288
289}  // namespace
290
291ServiceDiscoveryClientMdns::ServiceDiscoveryClientMdns()
292    : mdns_runner_(
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);
299  StartNewClient();
300}
301
302scoped_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));
308}
309
310scoped_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));
316}
317
318scoped_ptr<LocalDomainResolver>
319ServiceDiscoveryClientMdns::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));
326}
327
328ServiceDiscoveryClientMdns::~ServiceDiscoveryClientMdns() {
329  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
330  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
331  DCHECK(proxies_.empty());
332  Reset();
333}
334
335void 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();
341}
342
343void 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(
349        FROM_HERE,
350        base::Bind(&ServiceDiscoveryClientMdns::StartNewClient,
351                   weak_ptr_factory_.GetWeakPtr()),
352        base::TimeDelta::FromSeconds(
353            kRestartDelayOnNetworkChangeSeconds * (1 << restart_attempts_)));
354  } else {
355    ReportSuccess();
356  }
357}
358
359void ServiceDiscoveryClientMdns::StartNewClient() {
360  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361  ++restart_attempts_;
362  Reset();
363  mdns_.reset(net::MDnsClient::CreateDefault().release());
364  client_.reset(new ServiceDiscoveryClientImpl(mdns_.get()));
365  BrowserThread::PostTaskAndReplyWithResult(
366      BrowserThread::FILE,
367      FROM_HERE,
368      base::Bind(&net::GetMDnsInterfacesToBind),
369      base::Bind(&ServiceDiscoveryClientMdns::OnInterfaceListReady,
370                 weak_ptr_factory_.GetWeakPtr()));
371}
372
373void ServiceDiscoveryClientMdns::OnInterfaceListReady(
374    const net::InterfaceIndexFamilyList& interfaces) {
375  mdns_runner_->PostTask(
376      FROM_HERE,
377      base::Bind(&InitMdns,
378                 base::Bind(&ServiceDiscoveryClientMdns::OnMdnsInitialized,
379                            weak_ptr_factory_.GetWeakPtr()),
380                 interfaces,
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();
387}
388
389void ServiceDiscoveryClientMdns::OnMdnsInitialized(bool success) {
390  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
391  if (!success) {
392    ScheduleStartNewClient();
393    return;
394  }
395  ReportSuccess();
396
397  std::set<Proxy*> tmp_proxies(proxies_);
398  std::for_each(tmp_proxies.begin(), tmp_proxies.end(),
399                std::mem_fun(&Proxy::OnNewMdnsReady));
400}
401
402void ServiceDiscoveryClientMdns::ReportSuccess() {
403  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
404  UMA_HISTOGRAM_COUNTS_100("LocalDiscovery.ClientRestartAttempts",
405                           restart_attempts_);
406}
407
408void ServiceDiscoveryClientMdns::Reset() {
409  need_dalay_mdns_tasks_ = true;
410  delayed_tasks_.clear();
411
412  weak_ptr_factory_.InvalidateWeakPtrs();
413
414  std::for_each(proxies_.begin(), proxies_.end(),
415                std::mem_fun(&Proxy::OnMdnsDestroy));
416  if (client_)
417    mdns_runner_->DeleteSoon(FROM_HERE, client_.release());
418  if (mdns_)
419    mdns_runner_->DeleteSoon(FROM_HERE, mdns_.release());
420}
421
422bool 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);
430  return true;
431}
432
433}  // namespace local_discovery
434