Skip to content

Commit

Permalink
mdns: Fix/Update Windows mdns code
Browse files Browse the repository at this point in the history
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
rgetz committed Apr 24, 2022
1 parent 6bc7d67 commit d39f914
Showing 1 changed file with 235 additions and 59 deletions.
294 changes: 235 additions & 59 deletions dns_sd_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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,
Expand All @@ -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;

Expand Down Expand Up @@ -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]);
Expand All @@ -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);
Expand Down

0 comments on commit d39f914

Please sign in to comment.