blob: e9defaa41d8be1a26fcfaf0f72cea368b46a89ea [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PEERD_AVAHI_SERVICE_DISCOVERER_H_
#define PEERD_AVAHI_SERVICE_DISCOVERER_H_
#include <map>
#include <set>
#include <string>
#include <tuple>
#include <vector>
#include <base/memory/ref_counted.h>
#include <base/memory/weak_ptr.h>
#include <dbus/bus.h>
#include "peerd/service.h"
#include "peerd/typedefs.h"
namespace dbus {
class ObjectProxy;
class Signal;
} // namespace dbus
namespace peerd {
class PeerManagerInterface;
// Avahi allows consumers to discover services in two phases: browsing and
// resolution. Service discovery is done by requesting Avahi to create
// AvahiServiceBrowser objects for a particular service type (e.g.
// "_privet._tcp"). Inside our own process, we need to keep DBus proxies
// for those objects around.
//
// Then, we start getting signals for service creation/deletion from Avahi
// through our AvahiServiceBrowser objects. Avahi identifies services by a
// combination of:
// 1) The interface we've discovered that service on
// 2) The unique name of the service
// 3) The type of the service
// 4) The domain the service was discovered on.
//
// For each instance of a service, we then need to ask Avahi to create an
// AvahiServiceResolver to read the TXT record and signal changes to the TXT
// record. Again, we need to keep local DBus proxies for those remote
// objects.
//
// When we get a signal that a service instance is gone, we remove the
// resolver from Avahi. When we have no peers advertising a particular
// service type via root _serbus records, we remove the service browser for
// that type.
class AvahiServiceDiscoverer {
public:
AvahiServiceDiscoverer(const scoped_refptr<dbus::Bus>& bus,
dbus::ObjectProxy* avahi_proxy,
PeerManagerInterface* peer_manager_);
~AvahiServiceDiscoverer();
void RegisterAsync(const CompletionAction& completion_callback);
private:
using avahi_if_t = int32_t;
using avahi_proto_t = int32_t; // Either IPv4 or IPv6.
using TxtList = std::vector<std::vector<uint8_t>>; // DBus type aay.
// A resolver corrresponds to a particular name/type/domain/interface tuple,
// but we organize them by type for book keeping reasons, and so this is just
// a tuple<interface, name, domain>.
using resolv_key_t = std::tuple<avahi_if_t, std::string, std::string>;
using ResolversForType = std::map<resolv_key_t, dbus::ObjectProxy*>;
// A map of service types to the resolvers for that type.
using ResolverMap = std::map<std::string, ResolversForType>;
static bool txt_list2service_info(const TxtList& txt_list,
Service::ServiceInfo* info);
// Creates a new AvahiServiceBrowser, hooks up signals, returns it.
// |cb| is called asynchronously with the success or failure of
// signal registration.
dbus::ObjectProxy* BrowseServices(const std::string& service_type,
const CompletionAction& cb);
// Updates internal datastructures to reflect that the root serbus
// service for a peer has changed. This may cause us to stop browsing
// for one or more service types if no other peers claim to support
// those service types.
void OnPeerServicesChanged(const std::string& peer_id,
std::set<std::string> new_service_types);
void OnPeerServicesRemoved(const std::string& peer_id);
// Listen to changes in TXT records for a service.
void RegisterResolver(avahi_if_t interface,
const std::string& name,
const std::string& type,
const std::string& domain);
// Stop listening to TXT record changes.
void RemoveResolver(avahi_if_t interface,
const std::string& name,
const std::string& type,
const std::string& domain);
// Logic to respond to new services being discovered/removed.
void HandleItemNew(avahi_if_t interface,
avahi_proto_t protocol,
const std::string& name,
const std::string& type,
const std::string& domain,
uint32_t flags);
void HandleItemRemove(avahi_if_t interface,
avahi_proto_t protocol,
const std::string& name,
const std::string& type,
const std::string& domain,
uint32_t flags);
// Signals that Avahi has had some serious trouble.
void HandleFailure(const std::string& service_type,
const std::string& message);
// For notifications from our AvahiServiceResolver(s).
void HandleFound(dbus::Signal* signal);
// And in case we encounter failure in a Resolver...
void HandleResolverFailure(avahi_if_t interface,
const std::string& name,
const std::string& type,
const std::string& domain,
dbus::Signal* signal);
scoped_refptr<dbus::Bus> bus_;
dbus::ObjectProxy* avahi_proxy_;
PeerManagerInterface* peer_manager_;
avahi_proto_t protocol_; // We support one protocol per discoverer (IPv4).
dbus::ObjectProxy* serbus_browser_;
// A map from service type to the browser for that service type.
std::map<std::string, dbus::ObjectProxy*> browsers_;
// A map from root serbus record names to the corresponding unique peer id.
std::map<resolv_key_t, std::string> serbus_record_to_peer_id_;
// A map from service type to list of peer ids of peers advertising that
// service.
std::map<std::string, std::set<std::string>> peers_for_service_;
ResolverMap resolvers_;
// Should be last member to invalidate weak pointers in child objects
// and avoid callbacks while partially destroyed.
base::WeakPtrFactory<AvahiServiceDiscoverer> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(AvahiServiceDiscoverer);
};
} // namespace peerd
#endif // PEERD_AVAHI_SERVICE_DISCOVERER_H_