1 // Copyright 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.
5 #include "chrome/browser/local_discovery/service_discovery_host_client.h"
7 #include "chrome/common/local_discovery/local_discovery_messages.h"
8 #include "chrome/grit/generated_resources.h"
9 #include "content/public/browser/browser_thread.h"
10 #include "content/public/browser/utility_process_host.h"
11 #include "net/dns/mdns_client.h"
12 #include "net/socket/socket_descriptor.h"
13 #include "ui/base/l10n/l10n_util.h"
16 #include <netinet/in.h>
17 #include "base/file_descriptor_posix.h"
20 namespace local_discovery
{
22 using content::BrowserThread
;
23 using content::UtilityProcessHost
;
28 SocketInfoList
GetSocketsOnFileThread() {
29 net::InterfaceIndexFamilyList
interfaces(net::GetMDnsInterfacesToBind());
30 SocketInfoList sockets
;
31 for (size_t i
= 0; i
< interfaces
.size(); ++i
) {
32 DCHECK(interfaces
[i
].second
== net::ADDRESS_FAMILY_IPV4
||
33 interfaces
[i
].second
== net::ADDRESS_FAMILY_IPV6
);
34 base::FileDescriptor
socket_descriptor(
35 net::CreatePlatformSocket(
36 net::ConvertAddressFamily(interfaces
[i
].second
), SOCK_DGRAM
,
39 LOG_IF(ERROR
, socket_descriptor
.fd
== net::kInvalidSocket
)
40 << "Can't create socket, family=" << interfaces
[i
].second
;
41 if (socket_descriptor
.fd
!= net::kInvalidSocket
) {
42 LocalDiscoveryMsg_SocketInfo socket
;
43 socket
.descriptor
= socket_descriptor
;
44 socket
.interface_index
= interfaces
[i
].first
;
45 socket
.address_family
= interfaces
[i
].second
;
46 sockets
.push_back(socket
);
56 class ServiceDiscoveryHostClient::ServiceWatcherProxy
: public ServiceWatcher
{
58 ServiceWatcherProxy(ServiceDiscoveryHostClient
* host
,
59 const std::string
& service_type
,
60 const ServiceWatcher::UpdatedCallback
& callback
)
62 service_type_(service_type
),
63 id_(host_
->RegisterWatcherCallback(callback
)),
67 ~ServiceWatcherProxy() override
{
68 DVLOG(1) << "~ServiceWatcherProxy with id " << id_
;
69 host_
->UnregisterWatcherCallback(id_
);
71 host_
->Send(new LocalDiscoveryMsg_DestroyWatcher(id_
));
74 void Start() override
{
75 DVLOG(1) << "ServiceWatcher::Start with id " << id_
;
77 host_
->Send(new LocalDiscoveryMsg_StartWatcher(id_
, service_type_
));
81 void DiscoverNewServices(bool force_update
) override
{
82 DVLOG(1) << "ServiceWatcher::DiscoverNewServices with id " << id_
;
84 host_
->Send(new LocalDiscoveryMsg_DiscoverServices(id_
, force_update
));
87 void SetActivelyRefreshServices(bool actively_refresh_services
) override
{
88 DVLOG(1) << "ServiceWatcher::SetActivelyRefreshServices with id " << id_
;
90 host_
->Send(new LocalDiscoveryMsg_SetActivelyRefreshServices(
91 id_
, actively_refresh_services
));
94 std::string
GetServiceType() const override
{ return service_type_
; }
97 scoped_refptr
<ServiceDiscoveryHostClient
> host_
;
98 const std::string service_type_
;
103 class ServiceDiscoveryHostClient::ServiceResolverProxy
104 : public ServiceResolver
{
106 ServiceResolverProxy(ServiceDiscoveryHostClient
* host
,
107 const std::string
& service_name
,
108 const ServiceResolver::ResolveCompleteCallback
& callback
)
110 service_name_(service_name
),
111 id_(host
->RegisterResolverCallback(callback
)),
115 ~ServiceResolverProxy() override
{
116 DVLOG(1) << "~ServiceResolverProxy with id " << id_
;
117 host_
->UnregisterResolverCallback(id_
);
119 host_
->Send(new LocalDiscoveryMsg_DestroyResolver(id_
));
122 void StartResolving() override
{
123 DVLOG(1) << "ServiceResolverProxy::StartResolving with id " << id_
;
125 host_
->Send(new LocalDiscoveryMsg_ResolveService(id_
, service_name_
));
129 std::string
GetName() const override
{ return service_name_
; }
132 scoped_refptr
<ServiceDiscoveryHostClient
> host_
;
133 const std::string service_name_
;
138 class ServiceDiscoveryHostClient::LocalDomainResolverProxy
139 : public LocalDomainResolver
{
141 LocalDomainResolverProxy(ServiceDiscoveryHostClient
* host
,
142 const std::string
& domain
,
143 net::AddressFamily address_family
,
144 const LocalDomainResolver::IPAddressCallback
& callback
)
147 address_family_(address_family
),
148 id_(host
->RegisterLocalDomainResolverCallback(callback
)),
152 ~LocalDomainResolverProxy() override
{
153 DVLOG(1) << "~LocalDomainResolverProxy with id " << id_
;
154 host_
->UnregisterLocalDomainResolverCallback(id_
);
156 host_
->Send(new LocalDiscoveryMsg_DestroyLocalDomainResolver(id_
));
159 void Start() override
{
160 DVLOG(1) << "LocalDomainResolverProxy::Start with id " << id_
;
162 host_
->Send(new LocalDiscoveryMsg_ResolveLocalDomain(id_
, domain_
,
168 scoped_refptr
<ServiceDiscoveryHostClient
> host_
;
170 net::AddressFamily address_family_
;
175 ServiceDiscoveryHostClient::ServiceDiscoveryHostClient() : current_id_(0) {
176 callback_runner_
= base::MessageLoop::current()->message_loop_proxy();
177 io_runner_
= BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
);
180 ServiceDiscoveryHostClient::~ServiceDiscoveryHostClient() {
181 DCHECK(service_watcher_callbacks_
.empty());
182 DCHECK(service_resolver_callbacks_
.empty());
183 DCHECK(domain_resolver_callbacks_
.empty());
186 scoped_ptr
<ServiceWatcher
> ServiceDiscoveryHostClient::CreateServiceWatcher(
187 const std::string
& service_type
,
188 const ServiceWatcher::UpdatedCallback
& callback
) {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
190 return scoped_ptr
<ServiceWatcher
>(
191 new ServiceWatcherProxy(this, service_type
, callback
));
194 scoped_ptr
<ServiceResolver
> ServiceDiscoveryHostClient::CreateServiceResolver(
195 const std::string
& service_name
,
196 const ServiceResolver::ResolveCompleteCallback
& callback
) {
197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
198 return scoped_ptr
<ServiceResolver
>(
199 new ServiceResolverProxy(this, service_name
, callback
));
202 scoped_ptr
<LocalDomainResolver
>
203 ServiceDiscoveryHostClient::CreateLocalDomainResolver(
204 const std::string
& domain
,
205 net::AddressFamily address_family
,
206 const LocalDomainResolver::IPAddressCallback
& callback
) {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
208 return scoped_ptr
<LocalDomainResolver
>(new LocalDomainResolverProxy(
209 this, domain
, address_family
, callback
));
212 uint64
ServiceDiscoveryHostClient::RegisterWatcherCallback(
213 const ServiceWatcher::UpdatedCallback
& callback
) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
215 DCHECK(!ContainsKey(service_watcher_callbacks_
, current_id_
+ 1));
216 service_watcher_callbacks_
[++current_id_
] = callback
;
220 uint64
ServiceDiscoveryHostClient::RegisterResolverCallback(
221 const ServiceResolver::ResolveCompleteCallback
& callback
) {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
223 DCHECK(!ContainsKey(service_resolver_callbacks_
, current_id_
+ 1));
224 service_resolver_callbacks_
[++current_id_
] = callback
;
228 uint64
ServiceDiscoveryHostClient::RegisterLocalDomainResolverCallback(
229 const LocalDomainResolver::IPAddressCallback
& callback
) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
231 DCHECK(!ContainsKey(domain_resolver_callbacks_
, current_id_
+ 1));
232 domain_resolver_callbacks_
[++current_id_
] = callback
;
236 void ServiceDiscoveryHostClient::UnregisterWatcherCallback(uint64 id
) {
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
238 service_watcher_callbacks_
.erase(id
);
241 void ServiceDiscoveryHostClient::UnregisterResolverCallback(uint64 id
) {
242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
243 service_resolver_callbacks_
.erase(id
);
246 void ServiceDiscoveryHostClient::UnregisterLocalDomainResolverCallback(
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
249 domain_resolver_callbacks_
.erase(id
);
252 void ServiceDiscoveryHostClient::Start(
253 const base::Closure
& error_callback
) {
254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
255 DCHECK(!utility_host_
);
256 DCHECK(error_callback_
.is_null());
257 error_callback_
= error_callback
;
258 io_runner_
->PostTask(
260 base::Bind(&ServiceDiscoveryHostClient::StartOnIOThread
, this));
263 void ServiceDiscoveryHostClient::Shutdown() {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
265 io_runner_
->PostTask(
267 base::Bind(&ServiceDiscoveryHostClient::ShutdownOnIOThread
, this));
270 #if defined(OS_POSIX)
272 void ServiceDiscoveryHostClient::StartOnIOThread() {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
274 DCHECK(!utility_host_
);
275 BrowserThread::PostTaskAndReplyWithResult(
278 base::Bind(&GetSocketsOnFileThread
),
279 base::Bind(&ServiceDiscoveryHostClient::OnSocketsReady
, this));
282 void ServiceDiscoveryHostClient::OnSocketsReady(const SocketInfoList
& sockets
) {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
284 DCHECK(!utility_host_
);
285 utility_host_
= UtilityProcessHost::Create(
286 this, base::MessageLoopProxy::current().get())->AsWeakPtr();
287 utility_host_
->SetName(l10n_util::GetStringUTF16(
288 IDS_UTILITY_PROCESS_SERVICE_DISCOVERY_HANDLER_NAME
));
289 utility_host_
->EnableMDns();
290 utility_host_
->StartBatchMode();
291 if (sockets
.empty()) {
292 ShutdownOnIOThread();
295 utility_host_
->Send(new LocalDiscoveryMsg_SetSockets(sockets
));
296 // Send messages for requests made during network enumeration.
297 for (size_t i
= 0; i
< delayed_messages_
.size(); ++i
)
298 utility_host_
->Send(delayed_messages_
[i
]);
299 delayed_messages_
.weak_clear();
304 void ServiceDiscoveryHostClient::StartOnIOThread() {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
306 DCHECK(!utility_host_
);
307 utility_host_
= UtilityProcessHost::Create(
308 this, base::MessageLoopProxy::current().get())->AsWeakPtr();
309 utility_host_
->SetName(l10n_util::GetStringUTF16(
310 IDS_UTILITY_PROCESS_SERVICE_DISCOVERY_HANDLER_NAME
));
311 utility_host_
->EnableMDns();
312 utility_host_
->StartBatchMode();
313 // Windows does not enumerate networks here.
314 DCHECK(delayed_messages_
.empty());
319 void ServiceDiscoveryHostClient::ShutdownOnIOThread() {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
322 utility_host_
->Send(new LocalDiscoveryMsg_ShutdownLocalDiscovery
);
323 utility_host_
->EndBatchMode();
324 utility_host_
.reset();
326 error_callback_
= base::Closure();
329 void ServiceDiscoveryHostClient::Send(IPC::Message
* msg
) {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
331 io_runner_
->PostTask(
333 base::Bind(&ServiceDiscoveryHostClient::SendOnIOThread
, this, msg
));
336 void ServiceDiscoveryHostClient::SendOnIOThread(IPC::Message
* msg
) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
339 utility_host_
->Send(msg
);
341 delayed_messages_
.push_back(msg
);
345 void ServiceDiscoveryHostClient::OnProcessCrashed(int exit_code
) {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
347 DCHECK(!utility_host_
);
351 bool ServiceDiscoveryHostClient::OnMessageReceived(
352 const IPC::Message
& message
) {
354 IPC_BEGIN_MESSAGE_MAP(ServiceDiscoveryHostClient
, message
)
355 IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_Error
, OnError
)
356 IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_WatcherCallback
,
358 IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_ResolverCallback
,
360 IPC_MESSAGE_HANDLER(LocalDiscoveryHostMsg_LocalDomainResolverCallback
,
361 OnLocalDomainResolverCallback
)
362 IPC_MESSAGE_UNHANDLED(handled
= false)
363 IPC_END_MESSAGE_MAP()
367 void ServiceDiscoveryHostClient::InvalidateWatchers() {
368 WatcherCallbacks service_watcher_callbacks
;
369 service_watcher_callbacks_
.swap(service_watcher_callbacks
);
370 service_resolver_callbacks_
.clear();
371 domain_resolver_callbacks_
.clear();
373 for (WatcherCallbacks::iterator i
= service_watcher_callbacks
.begin();
374 i
!= service_watcher_callbacks
.end(); i
++) {
375 if (!i
->second
.is_null()) {
376 i
->second
.Run(ServiceWatcher::UPDATE_INVALIDATED
, "");
381 void ServiceDiscoveryHostClient::OnError() {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
383 if (!error_callback_
.is_null())
384 callback_runner_
->PostTask(FROM_HERE
, error_callback_
);
387 void ServiceDiscoveryHostClient::OnWatcherCallback(
389 ServiceWatcher::UpdateType update
,
390 const std::string
& service_name
) {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
392 callback_runner_
->PostTask(
394 base::Bind(&ServiceDiscoveryHostClient::RunWatcherCallback
, this, id
,
395 update
, service_name
));
398 void ServiceDiscoveryHostClient::OnResolverCallback(
400 ServiceResolver::RequestStatus status
,
401 const ServiceDescription
& description
) {
402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
403 callback_runner_
->PostTask(
405 base::Bind(&ServiceDiscoveryHostClient::RunResolverCallback
, this, id
,
406 status
, description
));
409 void ServiceDiscoveryHostClient::OnLocalDomainResolverCallback(
412 const net::IPAddressNumber
& ip_address_ipv4
,
413 const net::IPAddressNumber
& ip_address_ipv6
) {
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
415 callback_runner_
->PostTask(
417 base::Bind(&ServiceDiscoveryHostClient::RunLocalDomainResolverCallback
,
418 this, id
, success
, ip_address_ipv4
, ip_address_ipv6
));
421 void ServiceDiscoveryHostClient::RunWatcherCallback(
423 ServiceWatcher::UpdateType update
,
424 const std::string
& service_name
) {
425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
426 WatcherCallbacks::iterator it
= service_watcher_callbacks_
.find(id
);
427 if (it
!= service_watcher_callbacks_
.end() && !it
->second
.is_null())
428 it
->second
.Run(update
, service_name
);
431 void ServiceDiscoveryHostClient::RunResolverCallback(
433 ServiceResolver::RequestStatus status
,
434 const ServiceDescription
& description
) {
435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
436 ResolverCallbacks::iterator it
= service_resolver_callbacks_
.find(id
);
437 if (it
!= service_resolver_callbacks_
.end() && !it
->second
.is_null())
438 it
->second
.Run(status
, description
);
441 void ServiceDiscoveryHostClient::RunLocalDomainResolverCallback(
444 const net::IPAddressNumber
& ip_address_ipv4
,
445 const net::IPAddressNumber
& ip_address_ipv6
) {
446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
447 DomainResolverCallbacks::iterator it
= domain_resolver_callbacks_
.find(id
);
448 if (it
!= domain_resolver_callbacks_
.end() && !it
->second
.is_null())
449 it
->second
.Run(success
, ip_address_ipv4
, ip_address_ipv6
);
452 } // namespace local_discovery