Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNS-SD avahi #33

Merged
merged 16 commits into from
Feb 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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