Skip to content

Commit

Permalink
Merge pull request #33 from pothosware/dnssd_avahi
Browse files Browse the repository at this point in the history
DNS-SD avahi
  • Loading branch information
guruofquality authored Feb 13, 2018
2 parents 20f70c6 + 714af15 commit c9d0ae8
Show file tree
Hide file tree
Showing 14 changed files with 908 additions and 100 deletions.
5 changes: 5 additions & 0 deletions Changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Release 0.5.0 (pending)
==========================

- Support for DNS-SD publishing and discovery with avahi

Release 0.4.3 (pending)
==========================

Expand Down
73 changes: 58 additions & 15 deletions client/Registration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,71 @@
#include "SoapyClient.hpp"
#include "LogAcceptor.hpp"
#include "SoapySSDPEndpoint.hpp"
#include "SoapyMDNSEndpoint.hpp"
#include "SoapyURLUtils.hpp"
#include "SoapyRemoteDefs.hpp"
#include "SoapyRPCPacker.hpp"
#include "SoapyRPCUnpacker.hpp"
#include <SoapySDR/Registry.hpp>
#include <SoapySDR/Logger.hpp>
#include <memory>
#include <thread>
#include <future>

/***********************************************************************
* URL discovery
**********************************************************************/
static std::vector<std::string> getServerURLs(const int ipVer, const long timeoutUs)
{
//connect to DNS-SD daemon and maintain a global connection
//logic will reconnect if the status has failed for some reason
static std::unique_ptr<SoapyMDNSEndpoint> mdnsEndpoint(new SoapyMDNSEndpoint());
if (not mdnsEndpoint->status()) mdnsEndpoint.reset(new SoapyMDNSEndpoint());

//On non-windows platforms the endpoint instance can last the
//duration of the process because it can be cleaned up safely.
//Windows has issues cleaning up threads and sockets on exit.
#ifndef _MSC_VER
static
#endif //_MSC_VER
std::unique_ptr<SoapySSDPEndpoint> ssdpEndpoint(new SoapySSDPEndpoint());

//get all IPv4 and IPv6 URLs because we will fallback
//to the other protocol if the server was found,
//but not under the preferred IP protocol version.
auto mdnsUrls = std::async(std::launch::async, &SoapyMDNSEndpoint::getServerURLs, mdnsEndpoint.get(), SOAPY_REMOTE_IPVER_UNSPEC, timeoutUs);
auto ssdpUrls = std::async(std::launch::async, &SoapySSDPEndpoint::getServerURLs, ssdpEndpoint.get(), SOAPY_REMOTE_IPVER_UNSPEC, timeoutUs);

//merge the results from the two discovery protocols
auto uuidToUrl = ssdpUrls.get();
for (const auto &uuidToMap : mdnsUrls.get())
{
for (const auto &verToUrl : uuidToMap.second)
{
uuidToUrl[uuidToMap.first][verToUrl.first] = verToUrl.second;
}
}

//select the URL according to the ipVersion preference
std::vector<std::string> serverUrls;
for (const auto &uuidToMap : uuidToUrl)
{
//prefer version match when found
auto itVer = uuidToMap.second.find(ipVer);
if (itVer != uuidToMap.second.end())
{
serverUrls.push_back(itVer->second);
}

//otherwise fall-back to any discovery
else if (not uuidToMap.second.empty())
{
serverUrls.push_back(uuidToMap.second.begin()->second);
}
}
return serverUrls;
}

/***********************************************************************
* Args translator for nested keywords
**********************************************************************/
Expand Down Expand Up @@ -64,28 +120,14 @@ static std::vector<SoapySDR::Kwargs> findRemote(const SoapySDR::Kwargs &args)
//no remote specified, use the discovery protocol
if (args.count("remote") == 0)
{
//On non-windows platforms the endpoint instance can last the
//duration of the process because it can be cleaned up safely.
//Windows has issues cleaning up threads and sockets on exit.
#ifndef _MSC_VER
static
#endif //_MSC_VER
auto ssdpEndpoint = SoapySSDPEndpoint::getInstance();

//enable forces new search queries
ssdpEndpoint->enablePeriodicSearch(true);

//wait maximum timeout for replies
std::this_thread::sleep_for(std::chrono::microseconds(timeoutUs));

//determine IP version preferences
int ipVer(4);
const auto ipVerIt = args.find("remote:ipver");
if (ipVerIt != args.end()) ipVer = std::stoi(ipVerIt->second);

//spawn futures to connect to each remote
std::vector<std::future<SoapySDR::KwargsList>> futures;
for (const auto &url : SoapySSDPEndpoint::getInstance()->getServerURLs(ipVer))
for (const auto &url : getServerURLs(ipVer, timeoutUs))
{
auto argsWithURL = args;
argsWithURL["remote"] = url;
Expand All @@ -104,6 +146,7 @@ static std::vector<SoapySDR::Kwargs> findRemote(const SoapySDR::Kwargs &args)

//otherwise connect to a specific url and enumerate
auto url = SoapyURL(args.at("remote"));
SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyClient querying devices for %s", url.toString().c_str());

//default url parameters when not specified
if (url.getScheme().empty()) url.setScheme("tcp");
Expand Down
18 changes: 18 additions & 0 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ if (WIN32)
list(APPEND SoapySDR_LIBRARIES ws2_32)
endif (WIN32)

#avahi for discovery over dnssd
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
find_package(Avahi)
endif ()

if (APPLE)
list(APPEND COMMON_SOURCES SoapyMDNSEndpointApple.cpp)
elseif (AVAHI_FOUND)
message(STATUS "AVAHI_INCLUDE_DIRS=${AVAHI_INCLUDE_DIRS}")
message(STATUS "AVAHI_LIBRARIES=${AVAHI_LIBRARIES}")
include_directories(${AVAHI_INCLUDE_DIRS})
list(APPEND SoapySDR_LIBRARIES ${AVAHI_LIBRARIES})
list(APPEND COMMON_SOURCES SoapyMDNSEndpointAvahi.cpp)
else ()
list(APPEND COMMON_SOURCES SoapyMDNSEndpointNone.cpp)
endif ()

#create private include header for network compatibility
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(
Expand Down
9 changes: 9 additions & 0 deletions common/FindAvahi.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
find_library(AVAHI_LIBRARY-COMMON NAMES avahi-common)
find_library(AVAHI_LIBRARY-CLIENT NAMES avahi-client)
find_path(AVAHI_INCLUDE_DIR avahi-client/publish.h)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Avahi DEFAULT_MSG AVAHI_LIBRARY-COMMON AVAHI_LIBRARY-CLIENT AVAHI_INCLUDE_DIR)
if(AVAHI_FOUND)
set(AVAHI_LIBRARIES ${AVAHI_LIBRARY-COMMON} ${AVAHI_LIBRARY-CLIENT})
set(AVAHI_INCLUDE_DIRS ${AVAHI_INCLUDE_DIR})
endif()
49 changes: 49 additions & 0 deletions common/SoapyMDNSEndpoint.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2018-2018 Josh Blum
// SPDX-License-Identifier: BSL-1.0

#pragma once
#include <string>
#include <map>

#define SOAPY_REMOTE_DNSSD_NAME "SoapyRemote"

#define SOAPY_REMOTE_DNSSD_TYPE "_soapy._tcp"

struct SoapyMDNSEndpointData;

/*!
* The DNS-SD client ties into the system's mDNS daemon.
* Used for both server side for publishing,
* and the client side for browsing/lookup.
*/
class SoapyMDNSEndpoint
{
public:

//! Connect to the daemon
SoapyMDNSEndpoint(void);

//! Disconnect from the daemon
~SoapyMDNSEndpoint(void);

//! Print information about the client
void printInfo(void);

//! Is the client connected and operational?
bool status(void);

/*!
* Allow the endpoint to advertise that its running the RPC service
*/
void registerService(const std::string &uuid, const std::string &service, const int ipVer);

/*!
* Get a list of all active server URLs.
* \param ipVer the preferred IP version to discover
* \return a mapping of server UUIDs to host URLs
*/
std::map<std::string, std::map<int, std::string>> getServerURLs(const int ipVer, const long timeoutUs);

private:
SoapyMDNSEndpointData *_impl;
};
Loading

0 comments on commit c9d0ae8

Please sign in to comment.