Skip to content

Commit d39f914

Browse files
committed
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 <[email protected]>
1 parent 6bc7d67 commit d39f914

File tree

1 file changed

+235
-59
lines changed

1 file changed

+235
-59
lines changed

dns_sd_windows.c

+235-59
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
/*
33
* libiio - Library for interfacing industrial I/O (IIO) devices
44
*
5-
* Copyright (C) 2014-2020 Analog Devices, Inc.
5+
* Copyright (C) 2014-2022 Analog Devices, Inc.
66
* Author: Adrian Suciu <[email protected]>
77
*
8-
* Based on https://github.com/mjansson/mdns/blob/ce2e4f789f06429008925ff8f18c22036e60201e/mdns.c
9-
* which is Licensed under Public Domain
8+
* Based on https://github.com/mjansson/mdns/blob/main/mdns.c
9+
* which should be sync'ed with the mdns.h file and is Licensed under Public Domain
1010
*/
1111

1212
#include <stdio.h>
@@ -47,6 +47,50 @@ static bool is_localhost4(const struct sockaddr_in *saddr)
4747
saddr->sin_addr.S_un.S_un_b.s_b4 == 1;
4848
}
4949

50+
static struct dns_sd_discovery_data *new_discovery_data(struct dns_sd_discovery_data *dd)
51+
{
52+
struct dns_sd_discovery_data *d;
53+
54+
d = zalloc(sizeof(*d));
55+
if (!d)
56+
return NULL;
57+
58+
if (dd)
59+
d->lock = dd->lock;
60+
61+
return d;
62+
}
63+
64+
static mdns_string_t ip_address_to_string(char *buffer, size_t capacity,
65+
const struct sockaddr *addr, size_t addrlen)
66+
{
67+
char host[NI_MAXHOST] = { 0 };
68+
char service[NI_MAXSERV] = { 0 };
69+
int ret, len = 0;
70+
ret = getnameinfo((const struct sockaddr *)addr, (socklen_t)addrlen, host,
71+
NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST);
72+
73+
if (ret == 0) {
74+
if (addr->sa_family == AF_INET6 &&
75+
((struct sockaddr_in6 *)addr)->sin6_port != 0 &&
76+
strncmp(service, "5353", sizeof("5353")))
77+
len = snprintf(buffer, capacity, "[%s]:%s", host, service);
78+
else if (((struct sockaddr_in *)addr)->sin_port != 0 &&
79+
strncmp(service, "5353", sizeof("5353")))
80+
len = snprintf(buffer, capacity, "%s:%s", host, service);
81+
else
82+
len = snprintf(buffer, capacity, "%s", host);
83+
}
84+
85+
if (len >= (int)capacity)
86+
len = (int)capacity - 1;
87+
mdns_string_t str;
88+
str.str = buffer;
89+
str.length = len;
90+
91+
return str;
92+
}
93+
5094
static int open_client_sockets(int *sockets, unsigned int max_sockets)
5195
{
5296
IP_ADAPTER_UNICAST_ADDRESS *unicast;
@@ -99,6 +143,7 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets)
99143

100144
if (!is_localhost4(saddr) &&
101145
num_sockets < max_sockets) {
146+
saddr->sin_port = htons((unsigned short)MDNS_PORT);
102147
sock = mdns_socket_open_ipv4(saddr);
103148
if (sock >= 0)
104149
sockets[num_sockets++] = sock;
@@ -113,6 +158,7 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets)
113158
if (unicast->DadState == NldsPreferred &&
114159
!is_localhost6(saddr6) &&
115160
num_sockets < max_sockets) {
161+
saddr6->sin6_port = htons((unsigned short)MDNS_PORT);
116162
sock = mdns_socket_open_ipv6(saddr6);
117163
if (sock >= 0)
118164
sockets[num_sockets++] = sock;
@@ -130,6 +176,14 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets)
130176
return num_sockets;
131177
}
132178

179+
/* We should get:
180+
* - "service" record (SRV) specifying host (name) and port
181+
* - IPv4 "address" record (A) specifying IPv4 address of a given host
182+
* - IPv6 "address" record (AAAA) specifying IPv6 address of a given host
183+
* It's this routine that gets called, and needs to stitch things together
184+
* The DNS host doesn't necessary need to be the acutal host (but for
185+
* mdns - it usually is.
186+
*/
133187
static int query_callback(int sock, const struct sockaddr *from, size_t addrlen,
134188
mdns_entry_type_t entry, uint16_t query_id,
135189
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,
140194
{
141195
struct dns_sd_discovery_data *dd = user_data;
142196
char addrbuffer[64];
143-
char servicebuffer[64];
197+
char entrybuffer[256];
144198
char namebuffer[256];
145199
mdns_record_srv_t srv;
200+
bool found = false;
201+
202+
mdns_string_t entrystr, fromaddrstr;
146203

147204
if (!dd) {
148205
IIO_ERROR("DNS SD: Missing info structure. Stop browsing.\n");
149206
goto quit;
150207
}
151208

152-
if (rtype != MDNS_RECORDTYPE_SRV)
209+
if (rtype != MDNS_RECORDTYPE_SRV && rtype != MDNS_RECORDTYPE_A &&
210+
rtype != MDNS_RECORDTYPE_AAAA)
153211
goto quit;
154212

155-
getnameinfo(from, (socklen_t)addrlen, addrbuffer, NI_MAXHOST,
156-
servicebuffer, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST);
157-
158-
srv = mdns_record_parse_srv(data, size, name_offset, name_length,
159-
namebuffer, sizeof(namebuffer));
160-
IIO_DEBUG("%s : SRV %.*s priority %d weight %d port %d\n", addrbuffer,
161-
MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port);
162-
163-
/* Go to the last element in the list */
164-
while (dd->next)
165-
dd = dd->next;
166-
167-
if (srv.name.length > 1)
168-
{
169-
dd->hostname = malloc(srv.name.length);
170-
if (!dd->hostname)
171-
return -ENOMEM;
213+
if (entry != MDNS_ENTRYTYPE_ANSWER)
214+
goto quit;
172215

173-
iio_strlcpy(dd->hostname, srv.name.str, srv.name.length);
174-
}
216+
entrystr = mdns_string_extract(data, size, &name_offset,
217+
entrybuffer, sizeof(entrybuffer));
175218

176-
iio_strlcpy(dd->addr_str, addrbuffer, DNS_SD_ADDRESS_STR_MAX);
177-
dd->port = srv.port;
219+
if (!strstr(entrystr.str, "_iio._tcp.local"))
220+
goto quit;
178221

179-
IIO_DEBUG("DNS SD: added %s (%s:%d)\n",
180-
dd->hostname, dd->addr_str, dd->port);
222+
fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer),
223+
from, addrlen);
224+
225+
iio_mutex_lock(dd->lock);
226+
if (rtype == MDNS_RECORDTYPE_SRV) {
227+
srv = mdns_record_parse_srv(data, size, record_offset, record_length,
228+
namebuffer, sizeof(namebuffer));
229+
IIO_DEBUG("%.*s : %.*s SRV %.*s priority %d weight %d port %d\n",
230+
MDNS_STRING_FORMAT(fromaddrstr), MDNS_STRING_FORMAT(entrystr),
231+
MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port);
232+
233+
/* find a match based on name/port/ipv[46] & update it, otherwise add it */
234+
while (dd->next) {
235+
if (dd->hostname &&
236+
!strncmp(dd->hostname, srv.name.str, srv.name.length - 1) &&
237+
(!dd->port || dd->port == srv.port) &&
238+
dd->found == (from->sa_family != AF_INET)) {
239+
dd->port = srv.port;
240+
IIO_DEBUG("DNS SD: updated SRV %s (%s port:%hu)\n",
241+
dd->hostname, dd->addr_str, dd->port);
242+
found = true;
243+
}
244+
dd = dd->next;
245+
}
246+
if (!found) {
247+
/* new hostname and port */
248+
if (srv.name.length > 1) {
249+
dd->hostname = iio_strndup(srv.name.str,
250+
srv.name.length - 1);
251+
if (!dd->hostname)
252+
goto mem_fail;
253+
}
181254

182-
/* A list entry was filled, prepare new item on the list */
183-
dd->next = zalloc(sizeof(*dd->next));
184-
if (!dd->next)
185-
IIO_ERROR("DNS SD mDNS Resolver : memory failure\n");
255+
iio_strlcpy(dd->addr_str, fromaddrstr.str, fromaddrstr.length + 1);
256+
dd->port = srv.port;
257+
dd->found = (from->sa_family != AF_INET);
258+
IIO_DEBUG("DNS SD: added SRV %s (%s port:%d)\n",
259+
dd->hostname, dd->addr_str, dd->port);
186260

261+
/* A list entry was filled, prepare new item on the list */
262+
dd->next = new_discovery_data(dd);
263+
if (!dd->next)
264+
goto mem_fail;
265+
}
266+
} else if (rtype == MDNS_RECORDTYPE_A) {
267+
struct sockaddr_in addr;
268+
mdns_string_t addrstr;
269+
270+
mdns_record_parse_a(data, size, record_offset, record_length, &addr);
271+
addrstr = ip_address_to_string(namebuffer, sizeof(namebuffer),
272+
(struct sockaddr *) &addr, sizeof(addr));
273+
IIO_DEBUG("%.*s : %.*s A %.*s\n", MDNS_STRING_FORMAT(fromaddrstr),
274+
MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(addrstr));
275+
276+
/* find a match based on name/ipv4 or 6 & update it, otherwise add it */
277+
while (dd->next) {
278+
if (dd->hostname &&
279+
!strncmp(dd->hostname, entrystr.str, entrystr.length - 1) &&
280+
!dd->found) {
281+
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1);
282+
IIO_DEBUG("DNS SD: updated A %s (%s port:%hu)\n",
283+
dd->hostname, dd->addr_str, dd->port);
284+
found = true;
285+
}
286+
dd = dd->next;
287+
}
288+
if (!found) {
289+
dd->hostname = iio_strndup(entrystr.str, entrystr.length - 1);
290+
if (!dd->hostname)
291+
goto mem_fail;
292+
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1);
293+
dd->found = 0;
294+
IIO_DEBUG("DNS SD: Added A %s (%s port:%hu)\n",
295+
dd->hostname, dd->addr_str, dd->port);
296+
/* A list entry was filled, prepare new item on the list */
297+
dd->next = new_discovery_data(dd);
298+
if (!dd->next)
299+
goto mem_fail;
300+
}
301+
}
302+
#ifdef HAVE_IPV6
303+
else if (rtype == MDNS_RECORDTYPE_AAAA) {
304+
struct sockaddr_in6 addr;
305+
mdns_string_t addrstr;
306+
unsigned short port = 0;
307+
308+
mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr);
309+
addrstr = ip_address_to_string(namebuffer, sizeof(namebuffer),
310+
(struct sockaddr *) &addr, sizeof(addr));
311+
IIO_DEBUG("%.*s : %.*s AAAA %.*s\n", MDNS_STRING_FORMAT(fromaddrstr),
312+
MDNS_STRING_FORMAT(entrystr), MDNS_STRING_FORMAT(addrstr));
313+
314+
/* find a match based on name/port/ipv[46] & update it, otherwise add it */
315+
while (dd->next) {
316+
if (dd->hostname &&
317+
!strncmp(dd->hostname, entrystr.str, entrystr.length - 1) &&
318+
dd->found) {
319+
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1);
320+
IIO_DEBUG("DNS SD: updated AAAA %s (%s port:%hu)\n",
321+
dd->hostname, dd->addr_str, dd->port);
322+
found = true;
323+
}
324+
else if (dd->hostname &&
325+
!strncmp(dd->hostname, entrystr.str, entrystr.length - 1)) {
326+
port = dd->port;
327+
}
328+
dd = dd->next;
329+
}
330+
if (!found) {
331+
dd->hostname = iio_strndup(entrystr.str, entrystr.length - 1);
332+
if (!dd->hostname)
333+
goto mem_fail;
334+
iio_strlcpy(dd->addr_str, addrstr.str, addrstr.length + 1);
335+
dd->found = 1;
336+
if (port)
337+
dd->port = port;
338+
IIO_DEBUG("DNS SD: added AAAA %s (%s port:%hu)\n",
339+
dd->hostname, dd->addr_str, dd->port);
340+
/* A list entry was filled, prepare new item on the list */
341+
dd->next = new_discovery_data(dd);
342+
if (!dd->next)
343+
goto mem_fail;
344+
}
345+
}
346+
#endif /* HAVE_IPV6 */
347+
iio_mutex_unlock(dd->lock);
187348
quit:
188349
return 0;
350+
351+
mem_fail:
352+
iio_mutex_unlock(dd->lock);
353+
IIO_ERROR("DNS SD mDNS Resolver : memory failure\n");
354+
return -ENOMEM;
189355
}
190356

191357
int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
192358
{
193359
WORD versionWanted = MAKEWORD(1, 1);
194360
WSADATA wsaData;
195361
const char service[] = "_iio._tcp.local";
196-
size_t records, capacity = 2048;
362+
size_t rec, records, capacity = 2048;
197363
struct dns_sd_discovery_data *d;
198-
unsigned int i, isock, num_sockets;
364+
unsigned int isock, num_sockets;
199365
void *buffer;
200366
int sockets[32];
201367
int transaction_id[32];
202-
int ret = -ENOMEM;
368+
int res, ret = -ENOMEM;
203369

204370
if (WSAStartup(versionWanted, &wsaData)) {
205-
printf("Failed to initialize WinSock\n");
371+
IIO_ERROR("Failed to initialize WinSock\n");
206372
return -WSAGetLastError();
207373
}
208374

209375
IIO_DEBUG("DNS SD: Start service discovery.\n");
210376

211-
d = zalloc(sizeof(*d));
377+
d = new_discovery_data(NULL);
212378
if (!d)
213379
goto out_wsa_cleanup;
380+
214381
/* pass the structure back, so it can be freed if err */
215382
*ddata = d;
216383

@@ -241,37 +408,43 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
241408
ret = mdns_query_send(sockets[isock], MDNS_RECORDTYPE_PTR,
242409
service, sizeof(service)-1, buffer,
243410
capacity, 0);
244-
if (ret <= 0)
411+
if (ret < 0)
245412
IIO_ERROR("Failed to send mDNS query: errno %d\n", errno);
246413

247414
transaction_id[isock] = ret;
248415
}
249416

250-
/* This is a simple implementation that loops for 10 seconds or as long as we get replies
251-
* A real world implementation would probably use select, poll or similar syscall to wait
252-
* until data is available on a socket and then read it */
417+
/* This is a simple implementation that loops as long as we get replies */
253418
IIO_DEBUG("Reading mDNS query replies\n");
254419

255-
for (i = 0; i < 10; i++) {
256-
do {
257-
records = 0;
258-
259-
for (isock = 0; isock < num_sockets; isock++) {
260-
if (transaction_id[isock] <= 0)
261-
continue;
420+
records = 0;
421+
do {
422+
struct timeval timeout;
423+
timeout.tv_sec = 2;
424+
timeout.tv_usec = 0;
425+
426+
int nfds = 0;
427+
fd_set readfs;
428+
FD_ZERO(&readfs);
429+
for (isock = 0; isock < num_sockets; ++isock) {
430+
if (sockets[isock] >= nfds)
431+
nfds = sockets[isock] + 1;
432+
FD_SET(sockets[isock], &readfs);
433+
}
262434

263-
records += mdns_query_recv(sockets[isock],
264-
buffer, capacity,
265-
query_callback, d,
266-
transaction_id[isock]);
435+
res = select(nfds, &readfs, 0, 0, &timeout);
436+
if (res > 0) {
437+
for (isock = 0; isock < num_sockets; ++isock) {
438+
if (FD_ISSET(sockets[isock], &readfs)) {
439+
rec = mdns_query_recv(sockets[isock], buffer, capacity, query_callback,
440+
d, transaction_id[isock]);
441+
if (rec > 0)
442+
records += rec;
443+
}
444+
FD_SET(sockets[isock], &readfs);
267445
}
268-
} while (records);
269-
270-
if (records)
271-
i = 0;
272-
273-
Sleep(100);
274-
}
446+
}
447+
} while (res > 0);
275448

276449
for (isock = 0; isock < num_sockets; ++isock)
277450
mdns_socket_close(sockets[isock]);
@@ -281,6 +454,9 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
281454
port_knock_discovery_data(&d);
282455
remove_dup_discovery_data(&d);
283456

457+
/* since d may have changed, make sure we pass back the start */
458+
*ddata = d;
459+
284460
ret = 0;
285461
out_free_buffer:
286462
free(buffer);

0 commit comments

Comments
 (0)