Skip to content

Commit

Permalink
Added Bonjour DNS SD implementation (macOS).
Browse files Browse the repository at this point in the history
Signed-off-by: Matej Kenda <[email protected]>
  • Loading branch information
matejk committed Feb 25, 2020
1 parent d4e1dbc commit 804216a
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 5 deletions.
25 changes: 20 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,29 @@ if(WITH_NETWORK_BACKEND)

find_library(AVAHI_CLIENT_LIBRARIES avahi-client)
find_library(AVAHI_COMMON_LIBRARIES avahi-common)
if(AVAHI_CLIENT_LIBRARIES AND AVAHI_COMMON_LIBRARIES)
message(STATUS "Building with Avahi, a zero-configuration networking (zeroconf) implementation")
set(HAVE_AVAHI ON)
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
find_library(CORE_SERVICES CoreServices)

message(STATUS "Building with CFNetServices, an Apple DNS SD implementation")
set(HAVE_DNS_SD ON)

list(APPEND LIBIIO_CFILES dns_sd_bonjour.c)
list(APPEND LIBS_TO_LINK ${CORE_SERVICES} )

elseif(AVAHI_CLIENT_LIBRARIES AND AVAHI_COMMON_LIBRARIES)
message(STATUS "Building with Avahi, a DNS SD implementation")
set(HAVE_DNS_SD ON)

list(APPEND LIBIIO_CFILES dns_sd_avahi.c)
set(AVAHI_LIBRARIES ${AVAHI_CLIENT_LIBRARIES} ${AVAHI_COMMON_LIBRARIES})
set(LIBS_TO_LINK ${LIBS_TO_LINK} ${AVAHI_LIBRARIES})
list(APPEND LIBS_TO_LINK ${AVAHI_LIBRARIES})
set(AVAHI_SERVICE_INSTALL_DIR /etc/avahi/services/ CACHE PATH "default install path for Avahi service files")
else()
message(STATUS "Building without Avahi (zeroconf) support")
message(STATUS "Building without DNS-SD (Zeroconf) support")
endif()

if (HAVE_DNS_SD)
add_definitions(-DHAVE_DNS_SD=1)
endif()

set(NEED_THREADS 1)
Expand Down
167 changes: 167 additions & 0 deletions dns_sd_bonjour.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* libiio - Library for interfacing industrial I/O (IIO) devices
*
* Copyright (C) 2020 Matej Kenda.
* Author: Matej Kenda <matejken<at>gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* */

#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <CFNetwork/CFNetwork.h>

#include "debug.h"

/*
Implementation for DNS SD discovery for macOS using CFNetServices.
*/

struct cfnet_discovery_data {
bool have_v4;
bool have_v6;
char address_v4[80];
char address_v6[80];
int16_t port;
};

static void __cfnet_browser_cb (
CFNetServiceBrowserRef browser,
CFOptionFlags flags,
CFTypeRef domainOrService,
CFStreamError* error,
void* info)
{
CFStreamError anError;

if ((flags & kCFNetServiceFlagIsDomain) != 0) {
ERROR("DNS SD: FATAL. Callback called for domain, not service.\n");
goto stop_browsing;
}

struct cfnet_discovery_data *dd = (struct cfnet_discovery_data *)info;
if (dd == NULL) {
ERROR("DNS SD: Missing info structure. Stop browsing.\n");
goto stop_browsing;
}

if (dd->have_v4 || dd->have_v6) {
// Already resolved one IIO service. Stop.
DEBUG("DNS SD: first service already resolved. Skip.\n");
goto stop_browsing;
}

const CFNetServiceRef netService = (CFNetServiceRef)domainOrService;
if (netService == NULL) {
ERROR("DNS SD: Net service is null.\n");
goto stop_browsing;
}

if (!CFNetServiceResolveWithTimeout(netService, 10.0, &anError)) {
ERROR("DNS SD: Resolve error: %ld.%d\n", anError.domain, anError.error);
goto stop_browsing;
}

dd->port = CFNetServiceGetPortNumber(netService);

CFArrayRef addrArr = CFNetServiceGetAddressing(netService);
if (addrArr == NULL) {
ERROR("DNS SD: No valid addresses for service.\n");
goto stop_browsing;
}

for (CFIndex i = 0; i < CFArrayGetCount(addrArr); i++) {
struct sockaddr_in *sa = (struct sockaddr_in *)
CFDataGetBytePtr(CFArrayGetValueAtIndex(addrArr, i));
switch(sa->sin_family) {
case AF_INET:
if (inet_ntop(sa->sin_family, &sa->sin_addr,
dd->address_v4, sizeof(dd->address_v4))) {
dd->have_v4 = TRUE;
}
case AF_INET6:
if (inet_ntop(sa->sin_family, &sa->sin_addr,
dd->address_v6, sizeof(dd->address_v6))) {
dd->have_v6 = TRUE;
}
}
}

if ((flags & kCFNetServiceFlagMoreComing) == 0) {
DEBUG("DNS SD: No more entries coming.\n");
goto stop_browsing;
}

return;

stop_browsing:

CFNetServiceBrowserStopSearch(browser, &anError);
}

int discover_host(char *addr_str, size_t addr_len, uint16_t *port)
{
int ret = 0;

struct cfnet_discovery_data ddata = { FALSE, FALSE };
CFNetServiceClientContext clientContext = { 0, &ddata, NULL, NULL, NULL };
CFStreamError error;
Boolean result;

CFStringRef type = CFSTR("_iio._tcp.");
CFStringRef domain = CFSTR("");

DEBUG("DNS SD: Resolving hostname.");

CFNetServiceBrowserRef gServiceBrowserRef = CFNetServiceBrowserCreate(
kCFAllocatorDefault, __cfnet_browser_cb, &clientContext);

if (gServiceBrowserRef == NULL) {
ERROR("DNS SD: Failed to create service browser.\n");
ret = ENOMEM;
goto exit;
}
result = CFNetServiceBrowserSearchForServices(
gServiceBrowserRef, domain, type, &error);

if (result == false) {
ERROR("DNS SD: CFNetServiceBrowserSearchForServices failed (domain = %ld, error = %d)\n",
(long)error.domain, error.error);
ret = ENXIO;
goto free_browser;
}

// Resolved address and port. ipv4 has precedence over ipv6.
*port = ddata.port;
if (ddata.have_v4) {
strncpy(addr_str, ddata.address_v4, addr_len);
}
else if (ddata.have_v6) {
strncpy(addr_str, ddata.address_v6, addr_len);
}
else {
ERROR("DNS SD: IIO service not found.\n");
ret = ENXIO;
}

free_browser:

CFRelease(gServiceBrowserRef);
gServiceBrowserRef = NULL;

exit:
return ret;
}

0 comments on commit 804216a

Please sign in to comment.