2
2
/*
3
3
* libiio - Library for interfacing industrial I/O (IIO) devices
4
4
*
5
- * Copyright (C) 2014-2020 Analog Devices, Inc.
5
+ * Copyright (C) 2014-2022 Analog Devices, Inc.
6
6
* Author: Adrian Suciu <[email protected] >
7
7
*
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
10
10
*/
11
11
12
12
#include <stdio.h>
@@ -47,6 +47,50 @@ static bool is_localhost4(const struct sockaddr_in *saddr)
47
47
saddr -> sin_addr .S_un .S_un_b .s_b4 == 1 ;
48
48
}
49
49
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
+
50
94
static int open_client_sockets (int * sockets , unsigned int max_sockets )
51
95
{
52
96
IP_ADAPTER_UNICAST_ADDRESS * unicast ;
@@ -99,6 +143,7 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets)
99
143
100
144
if (!is_localhost4 (saddr ) &&
101
145
num_sockets < max_sockets ) {
146
+ saddr -> sin_port = htons ((unsigned short )MDNS_PORT );
102
147
sock = mdns_socket_open_ipv4 (saddr );
103
148
if (sock >= 0 )
104
149
sockets [num_sockets ++ ] = sock ;
@@ -113,6 +158,7 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets)
113
158
if (unicast -> DadState == NldsPreferred &&
114
159
!is_localhost6 (saddr6 ) &&
115
160
num_sockets < max_sockets ) {
161
+ saddr6 -> sin6_port = htons ((unsigned short )MDNS_PORT );
116
162
sock = mdns_socket_open_ipv6 (saddr6 );
117
163
if (sock >= 0 )
118
164
sockets [num_sockets ++ ] = sock ;
@@ -130,6 +176,14 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets)
130
176
return num_sockets ;
131
177
}
132
178
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
+ */
133
187
static int query_callback (int sock , const struct sockaddr * from , size_t addrlen ,
134
188
mdns_entry_type_t entry , uint16_t query_id ,
135
189
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,
140
194
{
141
195
struct dns_sd_discovery_data * dd = user_data ;
142
196
char addrbuffer [64 ];
143
- char servicebuffer [ 64 ];
197
+ char entrybuffer [ 256 ];
144
198
char namebuffer [256 ];
145
199
mdns_record_srv_t srv ;
200
+ bool found = false;
201
+
202
+ mdns_string_t entrystr , fromaddrstr ;
146
203
147
204
if (!dd ) {
148
205
IIO_ERROR ("DNS SD: Missing info structure. Stop browsing.\n" );
149
206
goto quit ;
150
207
}
151
208
152
- if (rtype != MDNS_RECORDTYPE_SRV )
209
+ if (rtype != MDNS_RECORDTYPE_SRV && rtype != MDNS_RECORDTYPE_A &&
210
+ rtype != MDNS_RECORDTYPE_AAAA )
153
211
goto quit ;
154
212
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 ;
172
215
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 ));
175
218
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 ;
178
221
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
+ }
181
254
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 );
186
260
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 );
187
348
quit :
188
349
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 ;
189
355
}
190
356
191
357
int dnssd_find_hosts (struct dns_sd_discovery_data * * ddata )
192
358
{
193
359
WORD versionWanted = MAKEWORD (1 , 1 );
194
360
WSADATA wsaData ;
195
361
const char service [] = "_iio._tcp.local" ;
196
- size_t records , capacity = 2048 ;
362
+ size_t rec , records , capacity = 2048 ;
197
363
struct dns_sd_discovery_data * d ;
198
- unsigned int i , isock , num_sockets ;
364
+ unsigned int isock , num_sockets ;
199
365
void * buffer ;
200
366
int sockets [32 ];
201
367
int transaction_id [32 ];
202
- int ret = - ENOMEM ;
368
+ int res , ret = - ENOMEM ;
203
369
204
370
if (WSAStartup (versionWanted , & wsaData )) {
205
- printf ("Failed to initialize WinSock\n" );
371
+ IIO_ERROR ("Failed to initialize WinSock\n" );
206
372
return - WSAGetLastError ();
207
373
}
208
374
209
375
IIO_DEBUG ("DNS SD: Start service discovery.\n" );
210
376
211
- d = zalloc ( sizeof ( * d ) );
377
+ d = new_discovery_data ( NULL );
212
378
if (!d )
213
379
goto out_wsa_cleanup ;
380
+
214
381
/* pass the structure back, so it can be freed if err */
215
382
* ddata = d ;
216
383
@@ -241,37 +408,43 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
241
408
ret = mdns_query_send (sockets [isock ], MDNS_RECORDTYPE_PTR ,
242
409
service , sizeof (service )- 1 , buffer ,
243
410
capacity , 0 );
244
- if (ret <= 0 )
411
+ if (ret < 0 )
245
412
IIO_ERROR ("Failed to send mDNS query: errno %d\n" , errno );
246
413
247
414
transaction_id [isock ] = ret ;
248
415
}
249
416
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 */
253
418
IIO_DEBUG ("Reading mDNS query replies\n" );
254
419
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
+ }
262
434
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 );
267
445
}
268
- } while (records );
269
-
270
- if (records )
271
- i = 0 ;
272
-
273
- Sleep (100 );
274
- }
446
+ }
447
+ } while (res > 0 );
275
448
276
449
for (isock = 0 ; isock < num_sockets ; ++ isock )
277
450
mdns_socket_close (sockets [isock ]);
@@ -281,6 +454,9 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
281
454
port_knock_discovery_data (& d );
282
455
remove_dup_discovery_data (& d );
283
456
457
+ /* since d may have changed, make sure we pass back the start */
458
+ * ddata = d ;
459
+
284
460
ret = 0 ;
285
461
out_free_buffer :
286
462
free (buffer );
0 commit comments