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 7, 2022
1 parent 47381e2 commit 1204601
Showing 1 changed file with 201 additions and 36 deletions.
237 changes: 201 additions & 36 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,74 @@ 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 ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr,
size_t addrlen) {
char host[NI_MAXHOST] = { 0 };
char service[NI_MAXSERV] = { 0 };
int ret = getnameinfo((const struct sockaddr*)addr, (socklen_t)addrlen, host, NI_MAXHOST,
service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST);
int len = 0;
if (ret == 0) {
if (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;
}

#ifdef HAVE_IPV6
static mdns_string_t ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr,
size_t addrlen) {
char host[NI_MAXHOST] = { 0 };
char service[NI_MAXSERV] = { 0 };
int ret = getnameinfo((const struct sockaddr*)addr, (socklen_t)addrlen, host, NI_MAXHOST,
service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST);
int len = 0;
if (ret == 0) {
if (addr->sin6_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;
}
#endif /* HAVE_IPV6 */

static mdns_string_t ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr, size_t addrlen) {
#ifdef HAVE_IPV6
if (addr->sa_family == AF_INET6)
return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr, addrlen);
#endif /* HAVE_IPV6 */
return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr, addrlen);
}



static int open_client_sockets(int *sockets, unsigned int max_sockets)
{
IP_ADAPTER_UNICAST_ADDRESS *unicast;
Expand Down Expand Up @@ -130,6 +198,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,52 +216,140 @@ 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;

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);
}
mdns_string_t 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);
mdns_string_t 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/ipv4 or 6 & 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_record_parse_a(data, size, record_offset, record_length, &addr);
mdns_string_t addrstr =
ipv4_address_to_string(namebuffer, sizeof(namebuffer), &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;

/* 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_record_parse_aaaa(data, size, record_offset, record_length, &addr);
mdns_string_t addrstr =
ipv6_address_to_string(namebuffer, sizeof(namebuffer), &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/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 AAAA %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 = 1;

/* 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)
Expand All @@ -202,15 +366,16 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
int 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,7 +406,7 @@ 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;
Expand All @@ -257,7 +422,7 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
records = 0;

for (isock = 0; isock < num_sockets; isock++) {
if (transaction_id[isock] <= 0)
if (transaction_id[isock] < 0)
continue;

records += mdns_query_recv(sockets[isock],
Expand Down

0 comments on commit 1204601

Please sign in to comment.