-
Notifications
You must be signed in to change notification settings - Fork 319
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This syncs the application side with the updates in the mdns.h file from: https://github.com/mjansson/mdns/ and does things according to the specs. (build up info from both A, AAAA and SRV records), not just assuming that the DNS server is the host. Signed-off-by: Robin Getz <[email protected]>
- Loading branch information
Showing
1 changed file
with
235 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,11 +2,11 @@ | |
/* | ||
* libiio - Library for interfacing industrial I/O (IIO) devices | ||
* | ||
* Copyright (C) 2014-2020 Analog Devices, Inc. | ||
* Copyright (C) 2014-2022 Analog Devices, Inc. | ||
* Author: Adrian Suciu <[email protected]> | ||
* | ||
* Based on https://github.com/mjansson/mdns/blob/ce2e4f789f06429008925ff8f18c22036e60201e/mdns.c | ||
* which is Licensed under Public Domain | ||
* Based on https://github.com/mjansson/mdns/blob/main/mdns.c | ||
* which should be sync'ed with the mdns.h file and is Licensed under Public Domain | ||
*/ | ||
|
||
#include <stdio.h> | ||
|
@@ -47,6 +47,50 @@ static bool is_localhost4(const struct sockaddr_in *saddr) | |
saddr->sin_addr.S_un.S_un_b.s_b4 == 1; | ||
} | ||
|
||
static struct dns_sd_discovery_data *new_discovery_data(struct dns_sd_discovery_data *dd) | ||
{ | ||
struct dns_sd_discovery_data *d; | ||
|
||
d = zalloc(sizeof(*d)); | ||
if (!d) | ||
return NULL; | ||
|
||
if (dd) | ||
d->lock = dd->lock; | ||
|
||
return d; | ||
} | ||
|
||
static mdns_string_t ip_address_to_string(char *buffer, size_t capacity, | ||
const struct sockaddr *addr, size_t addrlen) | ||
{ | ||
char host[NI_MAXHOST] = { 0 }; | ||
char service[NI_MAXSERV] = { 0 }; | ||
int ret, len = 0; | ||
ret = getnameinfo((const struct sockaddr *)addr, (socklen_t)addrlen, host, | ||
NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST); | ||
|
||
if (ret == 0) { | ||
if (addr->sa_family == AF_INET6 && | ||
((struct sockaddr_in6 *)addr)->sin6_port != 0 && | ||
strncmp(service, "5353", sizeof("5353"))) | ||
len = snprintf(buffer, capacity, "[%s]:%s", host, service); | ||
else if (((struct sockaddr_in *)addr)->sin_port != 0 && | ||
strncmp(service, "5353", sizeof("5353"))) | ||
len = snprintf(buffer, capacity, "%s:%s", host, service); | ||
else | ||
len = snprintf(buffer, capacity, "%s", host); | ||
} | ||
|
||
if (len >= (int)capacity) | ||
len = (int)capacity - 1; | ||
mdns_string_t str; | ||
str.str = buffer; | ||
str.length = len; | ||
|
||
return str; | ||
} | ||
|
||
static int open_client_sockets(int *sockets, unsigned int max_sockets) | ||
{ | ||
IP_ADAPTER_UNICAST_ADDRESS *unicast; | ||
|
@@ -99,6 +143,7 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets) | |
|
||
if (!is_localhost4(saddr) && | ||
num_sockets < max_sockets) { | ||
saddr->sin_port = htons((unsigned short)MDNS_PORT); | ||
sock = mdns_socket_open_ipv4(saddr); | ||
if (sock >= 0) | ||
sockets[num_sockets++] = sock; | ||
|
@@ -113,6 +158,7 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets) | |
if (unicast->DadState == NldsPreferred && | ||
!is_localhost6(saddr6) && | ||
num_sockets < max_sockets) { | ||
saddr6->sin6_port = htons((unsigned short)MDNS_PORT); | ||
sock = mdns_socket_open_ipv6(saddr6); | ||
if (sock >= 0) | ||
sockets[num_sockets++] = sock; | ||
|
@@ -130,6 +176,14 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets) | |
return num_sockets; | ||
} | ||
|
||
/* We should get: | ||
* - "service" record (SRV) specifying host (name) and port | ||
* - IPv4 "address" record (A) specifying IPv4 address of a given host | ||
* - IPv6 "address" record (AAAA) specifying IPv6 address of a given host | ||
* It's this routine that gets called, and needs to stitch things together | ||
* The DNS host doesn't necessary need to be the acutal host (but for | ||
* mdns - it usually is. | ||
*/ | ||
static int query_callback(int sock, const struct sockaddr *from, size_t addrlen, | ||
mdns_entry_type_t entry, uint16_t query_id, | ||
uint16_t rtype, uint16_t rclass, uint32_t ttl, | ||
|
@@ -140,77 +194,190 @@ static int query_callback(int sock, const struct sockaddr *from, size_t addrlen, | |
{ | ||
struct dns_sd_discovery_data *dd = user_data; | ||
char addrbuffer[64]; | ||
char servicebuffer[64]; | ||
char entrybuffer[256]; | ||
char namebuffer[256]; | ||
mdns_record_srv_t srv; | ||
bool found = false; | ||
|
||
mdns_string_t entrystr, fromaddrstr; | ||
|
||
if (!dd) { | ||
IIO_ERROR("DNS SD: Missing info structure. Stop browsing.\n"); | ||
goto quit; | ||
} | ||
|
||
if (rtype != MDNS_RECORDTYPE_SRV) | ||
if (rtype != MDNS_RECORDTYPE_SRV && rtype != MDNS_RECORDTYPE_A && | ||
rtype != MDNS_RECORDTYPE_AAAA) | ||
goto quit; | ||
|
||
getnameinfo(from, (socklen_t)addrlen, addrbuffer, NI_MAXHOST, | ||
servicebuffer, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST); | ||
|
||
srv = mdns_record_parse_srv(data, size, name_offset, name_length, | ||
namebuffer, sizeof(namebuffer)); | ||
IIO_DEBUG("%s : SRV %.*s priority %d weight %d port %d\n", addrbuffer, | ||
MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); | ||
|
||
/* Go to the last element in the list */ | ||
while (dd->next) | ||
dd = dd->next; | ||
|
||
if (srv.name.length > 1) | ||
{ | ||
dd->hostname = malloc(srv.name.length); | ||
if (!dd->hostname) | ||
return -ENOMEM; | ||
if (entry != MDNS_ENTRYTYPE_ANSWER) | ||
goto quit; | ||
|
||
iio_strlcpy(dd->hostname, srv.name.str, srv.name.length); | ||
} | ||
entrystr = mdns_string_extract(data, size, &name_offset, | ||
entrybuffer, sizeof(entrybuffer)); | ||
|
||
iio_strlcpy(dd->addr_str, addrbuffer, DNS_SD_ADDRESS_STR_MAX); | ||
dd->port = srv.port; | ||
if (!strstr(entrystr.str, "_iio._tcp.local")) | ||
goto quit; | ||
|
||
IIO_DEBUG("DNS SD: added %s (%s:%d)\n", | ||
dd->hostname, dd->addr_str, dd->port); | ||
fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), | ||
from, addrlen); | ||
|
||
iio_mutex_lock(dd->lock); | ||
if (rtype == MDNS_RECORDTYPE_SRV) { | ||
srv = mdns_record_parse_srv(data, size, record_offset, record_length, | ||
namebuffer, sizeof(namebuffer)); | ||
IIO_DEBUG("%.*s : %.*s SRV %.*s priority %d weight %d port %d\n", | ||
MDNS_STRING_FORMAT(fromaddrstr), MDNS_STRING_FORMAT(entrystr), | ||
MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); | ||
|
||
/* find a match based on name/port/ipv[46] & update it, otherwise add it */ | ||
while (dd->next) { | ||
if (dd->hostname && | ||
!strncmp(dd->hostname, srv.name.str, srv.name.length - 1) && | ||
(!dd->port || dd->port == srv.port) && | ||
dd->found == (from->sa_family != AF_INET)) { | ||
dd->port = srv.port; | ||
IIO_DEBUG("DNS SD: updated SRV %s (%s port:%hu)\n", | ||
dd->hostname, dd->addr_str, dd->port); | ||
found = true; | ||
} | ||
dd = dd->next; | ||
} | ||
if (!found) { | ||
/* new hostname and port */ | ||
if (srv.name.length > 1) { | ||
dd->hostname = iio_strndup(srv.name.str, | ||
srv.name.length - 1); | ||
if (!dd->hostname) | ||
goto mem_fail; | ||
} | ||
|
||
/* A list entry was filled, prepare new item on the list */ | ||
dd->next = zalloc(sizeof(*dd->next)); | ||
if (!dd->next) | ||
IIO_ERROR("DNS SD mDNS Resolver : memory failure\n"); | ||
iio_strlcpy(dd->addr_str, fromaddrstr.str, fromaddrstr.length + 1); | ||
dd->port = srv.port; | ||
dd->found = (from->sa_family != AF_INET); | ||
IIO_DEBUG("DNS SD: added SRV %s (%s port:%d)\n", | ||
dd->hostname, dd->addr_str, dd->port); | ||
|
||
/* A list entry was filled, prepare new item on the list */ | ||
dd->next = new_discovery_data(dd); | ||
if (!dd->next) | ||
goto mem_fail; | ||
} | ||
} else if (rtype == MDNS_RECORDTYPE_A) { | ||
struct sockaddr_in addr; | ||
mdns_string_t addrstr; | ||
|
||
mdns_record_parse_a(data, size, record_offset, record_length, &addr); | ||
addrstr = ip_address_to_string(namebuffer, sizeof(namebuffer), | ||
(struct sockaddr *) &addr, sizeof(addr)); | ||
IIO_DEBUG("%.*s : %.*s A %.*s\n", MDNS_STRING_FORMAT(fromaddrstr), | ||
MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(addrstr)); | ||
|
||
/* find a match based on name/ipv4 or 6 & update it, otherwise add it */ | ||
while (dd->next) { | ||
if (dd->hostname && | ||
!strncmp(dd->hostname, entrystr.str, entrystr.length - 1) && | ||
!dd->found) { | ||
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1); | ||
IIO_DEBUG("DNS SD: updated A %s (%s port:%hu)\n", | ||
dd->hostname, dd->addr_str, dd->port); | ||
found = true; | ||
} | ||
dd = dd->next; | ||
} | ||
if (!found) { | ||
dd->hostname = iio_strndup(entrystr.str, entrystr.length - 1); | ||
if (!dd->hostname) | ||
goto mem_fail; | ||
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1); | ||
dd->found = 0; | ||
IIO_DEBUG("DNS SD: Added A %s (%s port:%hu)\n", | ||
dd->hostname, dd->addr_str, dd->port); | ||
/* A list entry was filled, prepare new item on the list */ | ||
dd->next = new_discovery_data(dd); | ||
if (!dd->next) | ||
goto mem_fail; | ||
} | ||
} | ||
#ifdef HAVE_IPV6 | ||
else if (rtype == MDNS_RECORDTYPE_AAAA) { | ||
struct sockaddr_in6 addr; | ||
mdns_string_t addrstr; | ||
unsigned short port = 0; | ||
|
||
mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr); | ||
addrstr = ip_address_to_string(namebuffer, sizeof(namebuffer), | ||
(struct sockaddr *) &addr, sizeof(addr)); | ||
IIO_DEBUG("%.*s : %.*s AAAA %.*s\n", MDNS_STRING_FORMAT(fromaddrstr), | ||
MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(addrstr)); | ||
|
||
/* find a match based on name/port/ipv[46] & update it, otherwise add it */ | ||
while (dd->next) { | ||
if (dd->hostname && | ||
!strncmp(dd->hostname, entrystr.str, entrystr.length - 1) && | ||
dd->found) { | ||
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1); | ||
IIO_DEBUG("DNS SD: updated AAAA %s (%s port:%hu)\n", | ||
dd->hostname, dd->addr_str, dd->port); | ||
found = true; | ||
} | ||
else if (dd->hostname && | ||
!strncmp(dd->hostname, entrystr.str, entrystr.length - 1)) { | ||
port = dd->port; | ||
} | ||
dd = dd->next; | ||
} | ||
if (!found) { | ||
dd->hostname = iio_strndup(entrystr.str, entrystr.length - 1); | ||
if (!dd->hostname) | ||
goto mem_fail; | ||
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1); | ||
dd->found = 1; | ||
if (port) | ||
dd->port = port; | ||
IIO_DEBUG("DNS SD: added AAAA %s (%s port:%hu)\n", | ||
dd->hostname, dd->addr_str, dd->port); | ||
/* A list entry was filled, prepare new item on the list */ | ||
dd->next = new_discovery_data(dd); | ||
if (!dd->next) | ||
goto mem_fail; | ||
} | ||
} | ||
#endif /* HAVE_IPV6 */ | ||
iio_mutex_unlock(dd->lock); | ||
quit: | ||
return 0; | ||
|
||
mem_fail: | ||
iio_mutex_unlock(dd->lock); | ||
IIO_ERROR("DNS SD mDNS Resolver : memory failure\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
int dnssd_find_hosts(struct dns_sd_discovery_data **ddata) | ||
{ | ||
WORD versionWanted = MAKEWORD(1, 1); | ||
WSADATA wsaData; | ||
const char service[] = "_iio._tcp.local"; | ||
size_t records, capacity = 2048; | ||
size_t rec, records, capacity = 2048; | ||
struct dns_sd_discovery_data *d; | ||
unsigned int i, isock, num_sockets; | ||
unsigned int isock, num_sockets; | ||
void *buffer; | ||
int sockets[32]; | ||
int transaction_id[32]; | ||
int ret = -ENOMEM; | ||
int res, ret = -ENOMEM; | ||
|
||
if (WSAStartup(versionWanted, &wsaData)) { | ||
printf("Failed to initialize WinSock\n"); | ||
IIO_ERROR("Failed to initialize WinSock\n"); | ||
return -WSAGetLastError(); | ||
} | ||
|
||
IIO_DEBUG("DNS SD: Start service discovery.\n"); | ||
|
||
d = zalloc(sizeof(*d)); | ||
d = new_discovery_data(NULL); | ||
if (!d) | ||
goto out_wsa_cleanup; | ||
|
||
/* pass the structure back, so it can be freed if err */ | ||
*ddata = d; | ||
|
||
|
@@ -241,37 +408,43 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata) | |
ret = mdns_query_send(sockets[isock], MDNS_RECORDTYPE_PTR, | ||
service, sizeof(service)-1, buffer, | ||
capacity, 0); | ||
if (ret <= 0) | ||
if (ret < 0) | ||
IIO_ERROR("Failed to send mDNS query: errno %d\n", errno); | ||
|
||
transaction_id[isock] = ret; | ||
} | ||
|
||
/* This is a simple implementation that loops for 10 seconds or as long as we get replies | ||
* A real world implementation would probably use select, poll or similar syscall to wait | ||
* until data is available on a socket and then read it */ | ||
/* This is a simple implementation that loops as long as we get replies */ | ||
IIO_DEBUG("Reading mDNS query replies\n"); | ||
|
||
for (i = 0; i < 10; i++) { | ||
do { | ||
records = 0; | ||
|
||
for (isock = 0; isock < num_sockets; isock++) { | ||
if (transaction_id[isock] <= 0) | ||
continue; | ||
records = 0; | ||
do { | ||
struct timeval timeout; | ||
timeout.tv_sec = 2; | ||
timeout.tv_usec = 0; | ||
|
||
int nfds = 0; | ||
fd_set readfs; | ||
FD_ZERO(&readfs); | ||
for (isock = 0; isock < num_sockets; ++isock) { | ||
if (sockets[isock] >= nfds) | ||
nfds = sockets[isock] + 1; | ||
FD_SET(sockets[isock], &readfs); | ||
} | ||
|
||
records += mdns_query_recv(sockets[isock], | ||
buffer, capacity, | ||
query_callback, d, | ||
transaction_id[isock]); | ||
res = select(nfds, &readfs, 0, 0, &timeout); | ||
if (res > 0) { | ||
for (isock = 0; isock < num_sockets; ++isock) { | ||
if (FD_ISSET(sockets[isock], &readfs)) { | ||
rec = mdns_query_recv(sockets[isock], buffer, capacity, query_callback, | ||
d, transaction_id[isock]); | ||
if (rec > 0) | ||
records += rec; | ||
} | ||
FD_SET(sockets[isock], &readfs); | ||
} | ||
} while (records); | ||
|
||
if (records) | ||
i = 0; | ||
|
||
Sleep(100); | ||
} | ||
} | ||
} while (res > 0); | ||
|
||
for (isock = 0; isock < num_sockets; ++isock) | ||
mdns_socket_close(sockets[isock]); | ||
|
@@ -281,6 +454,9 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata) | |
port_knock_discovery_data(&d); | ||
remove_dup_discovery_data(&d); | ||
|
||
/* since d may have changed, make sure we pass back the start */ | ||
*ddata = d; | ||
|
||
ret = 0; | ||
out_free_buffer: | ||
free(buffer); | ||
|