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 ;
@@ -130,6 +174,14 @@ static int open_client_sockets(int *sockets, unsigned int max_sockets)
130
174
return num_sockets ;
131
175
}
132
176
177
+ /* We should get:
178
+ * - "service" record (SRV) specifying host (name) and port
179
+ * - IPv4 "address" record (A) specifying IPv4 address of a given host
180
+ * - IPv6 "address" record (AAAA) specifying IPv6 address of a given host
181
+ * It's this routine that gets called, and needs to stitch things together
182
+ * The DNS host doesn't necessary need to be the acutal host (but for
183
+ * mdns - it usually is.
184
+ */
133
185
static int query_callback (int sock , const struct sockaddr * from , size_t addrlen ,
134
186
mdns_entry_type_t entry , uint16_t query_id ,
135
187
uint16_t rtype , uint16_t rclass , uint32_t ttl ,
@@ -140,52 +192,155 @@ static int query_callback(int sock, const struct sockaddr *from, size_t addrlen,
140
192
{
141
193
struct dns_sd_discovery_data * dd = user_data ;
142
194
char addrbuffer [64 ];
143
- char servicebuffer [ 64 ];
195
+ char entrybuffer [ 256 ];
144
196
char namebuffer [256 ];
145
197
mdns_record_srv_t srv ;
198
+ bool found = false;
199
+
200
+ mdns_string_t entrystr , fromaddrstr ;
146
201
147
202
if (!dd ) {
148
203
IIO_ERROR ("DNS SD: Missing info structure. Stop browsing.\n" );
149
204
goto quit ;
150
205
}
151
206
152
- if (rtype != MDNS_RECORDTYPE_SRV )
207
+ if (rtype != MDNS_RECORDTYPE_SRV && rtype != MDNS_RECORDTYPE_A &&
208
+ rtype != MDNS_RECORDTYPE_AAAA )
153
209
goto quit ;
154
210
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 ;
211
+ if (entry != MDNS_ENTRYTYPE_ANSWER )
212
+ goto quit ;
172
213
173
- iio_strlcpy ( dd -> hostname , srv . name . str , srv . name . length );
174
- }
214
+ entrystr = mdns_string_extract ( data , size , & name_offset ,
215
+ entrybuffer , sizeof ( entrybuffer ));
175
216
176
- iio_strlcpy ( dd -> addr_str , addrbuffer , DNS_SD_ADDRESS_STR_MAX );
177
- dd -> port = srv . port ;
217
+ if (! strstr ( entrystr . str , "_iio._tcp.local" ))
218
+ goto quit ;
178
219
179
- IIO_DEBUG ("DNS SD: added %s (%s:%d)\n" ,
180
- dd -> hostname , dd -> addr_str , dd -> port );
220
+ fromaddrstr = ip_address_to_string (addrbuffer , sizeof (addrbuffer ),
221
+ from , addrlen );
222
+
223
+ iio_mutex_lock (dd -> lock );
224
+ if (rtype == MDNS_RECORDTYPE_SRV ) {
225
+ srv = mdns_record_parse_srv (data , size , record_offset , record_length ,
226
+ namebuffer , sizeof (namebuffer ));
227
+ IIO_DEBUG ("%.*s : %.*s SRV %.*s priority %d weight %d port %d\n" ,
228
+ MDNS_STRING_FORMAT (fromaddrstr ), MDNS_STRING_FORMAT (entrystr ),
229
+ MDNS_STRING_FORMAT (srv .name ), srv .priority , srv .weight , srv .port );
230
+
231
+ /* find a match based on name/port/ipv[46] & update it, otherwise add it */
232
+ while (dd -> next ) {
233
+ if (dd -> hostname &&
234
+ !strncmp (dd -> hostname , srv .name .str , srv .name .length - 1 ) &&
235
+ (!dd -> port || dd -> port == srv .port ) &&
236
+ dd -> found == (from -> sa_family != AF_INET )) {
237
+ dd -> port = srv .port ;
238
+ IIO_DEBUG ("DNS SD: updated SRV %s (%s port:%hu)\n" ,
239
+ dd -> hostname , dd -> addr_str , dd -> port );
240
+ found = true;
241
+ }
242
+ dd = dd -> next ;
243
+ }
244
+ if (!found ) {
245
+ /* new hostname and port */
246
+ if (srv .name .length > 1 ) {
247
+ dd -> hostname = iio_strndup (srv .name .str ,
248
+ srv .name .length - 1 );
249
+ if (!dd -> hostname )
250
+ goto mem_fail ;
251
+ }
181
252
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" );
253
+ iio_strlcpy (dd -> addr_str , fromaddrstr .str , fromaddrstr .length + 1 );
254
+ dd -> port = srv .port ;
255
+ dd -> found = (from -> sa_family != AF_INET );
256
+ IIO_DEBUG ("DNS SD: added SRV %s (%s port:%d)\n" ,
257
+ dd -> hostname , dd -> addr_str , dd -> port );
186
258
259
+ /* A list entry was filled, prepare new item on the list */
260
+ dd -> next = new_discovery_data (dd );
261
+ if (!dd -> next )
262
+ goto mem_fail ;
263
+ }
264
+ } else if (rtype == MDNS_RECORDTYPE_A ) {
265
+ struct sockaddr_in addr ;
266
+ mdns_string_t addrstr ;
267
+
268
+ mdns_record_parse_a (data , size , record_offset , record_length , & addr );
269
+ addrstr = ip_address_to_string (namebuffer , sizeof (namebuffer ),
270
+ (struct sockaddr * ) & addr , sizeof (addr ));
271
+ IIO_DEBUG ("%.*s : %.*s A %.*s\n" , MDNS_STRING_FORMAT (fromaddrstr ),
272
+ MDNS_STRING_FORMAT (entrystr ), MDNS_STRING_FORMAT (addrstr ));
273
+
274
+ /* find a match based on name/ipv4 or 6 & update it, otherwise add it */
275
+ while (dd -> next ) {
276
+ if (dd -> hostname &&
277
+ !strncmp (dd -> hostname , entrystr .str , entrystr .length - 1 ) &&
278
+ !dd -> found ) {
279
+ iio_strlcpy (dd -> addr_str , addrstr .str , addrstr .length + 1 );
280
+ IIO_DEBUG ("DNS SD: updated A %s (%s port:%hu)\n" ,
281
+ dd -> hostname , dd -> addr_str , dd -> port );
282
+ found = true;
283
+ }
284
+ dd = dd -> next ;
285
+ }
286
+ if (!found ) {
287
+ dd -> hostname = iio_strndup (entrystr .str , entrystr .length - 1 );
288
+ if (!dd -> hostname )
289
+ goto mem_fail ;
290
+ iio_strlcpy (dd -> addr_str , addrstr .str , addrstr .length + 1 );
291
+ dd -> found = 0 ;
292
+
293
+ /* A list entry was filled, prepare new item on the list */
294
+ dd -> next = new_discovery_data (dd );
295
+ if (!dd -> next )
296
+ goto mem_fail ;
297
+ }
298
+ }
299
+ #ifdef HAVE_IPV6
300
+ else if (rtype == MDNS_RECORDTYPE_AAAA ) {
301
+ struct sockaddr_in6 addr ;
302
+ mdns_string_t addrstr ;
303
+
304
+ mdns_record_parse_aaaa (data , size , record_offset , record_length , & addr );
305
+ addrstr = ip_address_to_string (namebuffer , sizeof (namebuffer ),
306
+ (struct sockaddr * ) & addr , sizeof (addr ));
307
+ IIO_DEBUG ("%.*s : %.*s AAAA %.*s\n" , MDNS_STRING_FORMAT (fromaddrstr ),
308
+ MDNS_STRING_FORMAT (entrystr ), MDNS_STRING_FORMAT (addrstr ));
309
+
310
+ /* find a match based on name/port/ipv[46] & update it, otherwise add it */
311
+ while (dd -> next ) {
312
+ if (dd -> hostname &&
313
+ !strncmp (dd -> hostname , entrystr .str , entrystr .length - 1 ) &&
314
+ dd -> found ) {
315
+ iio_strlcpy (dd -> addr_str , addrstr .str , addrstr .length + 1 );
316
+ IIO_DEBUG ("DNS SD: updated AAAA %s (%s port:%hu)\n" ,
317
+ dd -> hostname , dd -> addr_str , dd -> port );
318
+ found = true;
319
+ }
320
+ dd = dd -> next ;
321
+ }
322
+ if (!found ) {
323
+ dd -> hostname = iio_strndup (entrystr .str , entrystr .length - 1 );
324
+ if (!dd -> hostname )
325
+ goto mem_fail ;
326
+ iio_strlcpy (dd -> addr_str , addrstr .str , addrstr .length + 1 );
327
+ dd -> found = 1 ;
328
+
329
+ /* A list entry was filled, prepare new item on the list */
330
+ dd -> next = new_discovery_data (dd );
331
+ if (!dd -> next )
332
+ goto mem_fail ;
333
+ }
334
+ }
335
+ #endif /* HAVE_IPV6 */
336
+ iio_mutex_unlock (dd -> lock );
187
337
quit :
188
338
return 0 ;
339
+
340
+ mem_fail :
341
+ iio_mutex_unlock (dd -> lock );
342
+ IIO_ERROR ("DNS SD mDNS Resolver : memory failure\n" );
343
+ return - ENOMEM ;
189
344
}
190
345
191
346
int dnssd_find_hosts (struct dns_sd_discovery_data * * ddata )
@@ -202,15 +357,16 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
202
357
int ret = - ENOMEM ;
203
358
204
359
if (WSAStartup (versionWanted , & wsaData )) {
205
- printf ("Failed to initialize WinSock\n" );
360
+ IIO_ERROR ("Failed to initialize WinSock\n" );
206
361
return - WSAGetLastError ();
207
362
}
208
363
209
364
IIO_DEBUG ("DNS SD: Start service discovery.\n" );
210
365
211
- d = zalloc ( sizeof ( * d ) );
366
+ d = new_discovery_data ( NULL );
212
367
if (!d )
213
368
goto out_wsa_cleanup ;
369
+
214
370
/* pass the structure back, so it can be freed if err */
215
371
* ddata = d ;
216
372
@@ -241,23 +397,24 @@ int dnssd_find_hosts(struct dns_sd_discovery_data **ddata)
241
397
ret = mdns_query_send (sockets [isock ], MDNS_RECORDTYPE_PTR ,
242
398
service , sizeof (service )- 1 , buffer ,
243
399
capacity , 0 );
244
- if (ret <= 0 )
400
+ if (ret < 0 )
245
401
IIO_ERROR ("Failed to send mDNS query: errno %d\n" , errno );
246
402
247
403
transaction_id [isock ] = ret ;
248
404
}
249
405
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 */
406
+ /* This is a simple implementation that loops as long as we get replies
407
+ * A better implementation would probably use select, poll or similar
408
+ * syscall to wait until data is available on a socket and then read it
409
+ */
253
410
IIO_DEBUG ("Reading mDNS query replies\n" );
254
411
255
412
for (i = 0 ; i < 10 ; i ++ ) {
256
413
do {
257
414
records = 0 ;
258
415
259
416
for (isock = 0 ; isock < num_sockets ; isock ++ ) {
260
- if (transaction_id [isock ] <= 0 )
417
+ if (transaction_id [isock ] < 0 )
261
418
continue ;
262
419
263
420
records += mdns_query_recv (sockets [isock ],
0 commit comments