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.
4
5#include "chrome/browser/extensions/api/mdns/mdns_api.h"
6
7#include <vector>
8
9#include "base/lazy_instance.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/common/extensions/api/mdns.h"
12
13namespace extensions {
14
15namespace mdns = api::mdns;
16
17namespace {
18
19// Whitelisted mDNS service types.
20const char kCastServiceType[] = "_googlecast._tcp.local";
21const char kPrivetServiceType[] = "_privet._tcp.local";
22const char kTestServiceType[] = "_testing._tcp.local";
23
24bool IsServiceTypeWhitelisted(const std::string& service_type) {
25  return service_type == kCastServiceType ||
26         service_type == kPrivetServiceType ||
27         service_type == kTestServiceType;
28}
29
30}  // namespace
31
32MDnsAPI::MDnsAPI(content::BrowserContext* context) : browser_context_(context) {
33  DCHECK(browser_context_);
34  EventRouter::Get(context)
35      ->RegisterObserver(this, mdns::OnServiceList::kEventName);
36}
37
38MDnsAPI::~MDnsAPI() {
39  if (dns_sd_registry_.get()) {
40    dns_sd_registry_->RemoveObserver(this);
41  }
42}
43
44// static
45MDnsAPI* MDnsAPI::Get(content::BrowserContext* context) {
46  return BrowserContextKeyedAPIFactory<MDnsAPI>::Get(context);
47}
48
49static base::LazyInstance<BrowserContextKeyedAPIFactory<MDnsAPI> > g_factory =
50    LAZY_INSTANCE_INITIALIZER;
51
52// static
53BrowserContextKeyedAPIFactory<MDnsAPI>* MDnsAPI::GetFactoryInstance() {
54  return g_factory.Pointer();
55}
56
57void MDnsAPI::SetDnsSdRegistryForTesting(
58    scoped_ptr<DnsSdRegistry> dns_sd_registry) {
59  dns_sd_registry_ = dns_sd_registry.Pass();
60}
61
62DnsSdRegistry* MDnsAPI::dns_sd_registry() {
63  DCHECK(thread_checker_.CalledOnValidThread());
64  if (!dns_sd_registry_.get()) {
65    dns_sd_registry_.reset(new extensions::DnsSdRegistry());
66    dns_sd_registry_->AddObserver(this);
67  }
68  return dns_sd_registry_.get();
69}
70
71void MDnsAPI::OnListenerAdded(const EventListenerInfo& details) {
72  DCHECK(thread_checker_.CalledOnValidThread());
73  UpdateMDnsListeners(details);
74}
75
76void MDnsAPI::OnListenerRemoved(const EventListenerInfo& details) {
77  DCHECK(thread_checker_.CalledOnValidThread());
78  UpdateMDnsListeners(details);
79}
80
81void MDnsAPI::UpdateMDnsListeners(const EventListenerInfo& details) {
82  std::set<std::string> new_service_types;
83
84  // Check all listeners for service type filers.
85  const EventListenerMap::ListenerList& listeners =
86      extensions::EventRouter::Get(browser_context_)
87          ->listeners()
88          .GetEventListenersByName(details.event_name);
89  for (EventListenerMap::ListenerList::const_iterator it = listeners.begin();
90       it != listeners.end(); ++it) {
91    base::DictionaryValue* filter = ((*it)->filter());
92
93    std::string filter_value;
94    filter->GetStringASCII(kEventFilterServiceTypeKey, &filter_value);
95    if (filter_value.empty())
96      continue;
97    new_service_types.insert(filter_value);
98  }
99
100  // Find all the added and removed service types since last update.
101  std::set<std::string> added_service_types =
102      base::STLSetDifference<std::set<std::string> >(
103          new_service_types, service_types_);
104  std::set<std::string> removed_service_types =
105      base::STLSetDifference<std::set<std::string> >(
106          service_types_, new_service_types);
107
108  // Update the registry.
109  DnsSdRegistry* registry = dns_sd_registry();
110  for (std::set<std::string>::iterator it = added_service_types.begin();
111       it != added_service_types.end(); ++it) {
112    if (IsServiceTypeWhitelisted(*it))
113      registry->RegisterDnsSdListener(*it);
114  }
115  for (std::set<std::string>::iterator it = removed_service_types.begin();
116       it != removed_service_types.end(); ++it) {
117    if (IsServiceTypeWhitelisted(*it))
118      registry->UnregisterDnsSdListener(*it);
119  }
120
121  service_types_ = new_service_types;
122}
123
124void MDnsAPI::OnDnsSdEvent(const std::string& service_type,
125                           const DnsSdRegistry::DnsSdServiceList& services) {
126  DCHECK(thread_checker_.CalledOnValidThread());
127
128  std::vector<linked_ptr<mdns::MDnsService> > args;
129  for (DnsSdRegistry::DnsSdServiceList::const_iterator it = services.begin();
130       it != services.end(); ++it) {
131    linked_ptr<mdns::MDnsService> mdns_service =
132        make_linked_ptr(new mdns::MDnsService);
133    mdns_service->service_name = (*it).service_name;
134    mdns_service->service_host_port = (*it).service_host_port;
135    mdns_service->ip_address = (*it).ip_address;
136    mdns_service->service_data = (*it).service_data;
137    args.push_back(mdns_service);
138  }
139
140  scoped_ptr<base::ListValue> results = mdns::OnServiceList::Create(args);
141  scoped_ptr<Event> event(
142      new Event(mdns::OnServiceList::kEventName, results.Pass()));
143  event->restrict_to_browser_context = browser_context_;
144  event->filter_info.SetServiceType(service_type);
145
146  VLOG(1) << "Broadcasting OnServiceList event: " << event.get();
147
148  // TODO(justinlin): To avoid having listeners without filters getting all
149  // events, modify API to have this event require filters.
150  extensions::EventRouter::Get(browser_context_)->BroadcastEvent(event.Pass());
151}
152
153}  // namespace extensions
154