From d39f914a64c8962a999026c88fe053da1ba6e762 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Wed, 6 Apr 2022 19:36:03 -0400 Subject: [PATCH] mdns: Fix/Update Windows mdns code 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 --- dns_sd_windows.c | 294 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 235 insertions(+), 59 deletions(-) diff --git a/dns_sd_windows.c b/dns_sd_windows.c index 971e0d773..1df522a6f 100644 --- a/dns_sd_windows.c +++ b/dns_sd_windows.c @@ -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 * - * 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 @@ -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,52 +194,164 @@ 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) @@ -193,24 +359,25 @@ 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);