libiio 0.23
Library for interfacing with IIO devices
/builddir/build/BUILD/libiio-0.23/mdns.h
1/* https://github.com/mjansson/mdns/blob/6381bc09ae32b66de913fa0c982c41d3e67b63d2/mdns.h
2 * mdns.h - mDNS/DNS-SD library - Public Domain - 2017 Mattias Jansson
3 *
4 * This library provides a cross-platform mDNS and DNS-SD library in C.
5 * The implementation is based on RFC 6762 and RFC 6763.
6 *
7 * The latest source code is always available at
8 *
9 * https://github.com/mjansson/mdns
10 *
11 * This library is put in the public domain; you can redistribute it and/or modify it without any
12 * restrictions.
13 *
14 */
15
16#pragma once
17
18#include <stdint.h>
19#include <stddef.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include <fcntl.h>
24#ifdef _WIN32
25#include <winsock2.h>
26#include <ws2tcpip.h>
27#define strncasecmp _strnicmp
28#else
29#include <unistd.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#endif
33
34#ifdef __cplusplus
35extern "C" {
36#endif
37
38#define MDNS_INVALID_POS ((size_t)-1)
39
40#define MDNS_STRING_CONST(s) (s), (sizeof((s)) - 1)
41#define MDNS_STRING_FORMAT(s) (int)((s).length), s.str
42
43#define MDNS_POINTER_OFFSET(p, ofs) ((void*)((char*)(p) + (ptrdiff_t)(ofs)))
44#define MDNS_POINTER_OFFSET_CONST(p, ofs) ((const void*)((const char*)(p) + (ptrdiff_t)(ofs)))
45#define MDNS_POINTER_DIFF(a, b) ((size_t)((const char*)(a) - (const char*)(b)))
46
47#define MDNS_PORT 5353
48
49enum mdns_record_type {
50 MDNS_RECORDTYPE_IGNORE = 0,
51 // Address
52 MDNS_RECORDTYPE_A = 1,
53 // Domain Name pointer
54 MDNS_RECORDTYPE_PTR = 12,
55 // Arbitrary text string
56 MDNS_RECORDTYPE_TXT = 16,
57 // IP6 Address [Thomson]
58 MDNS_RECORDTYPE_AAAA = 28,
59 // Server Selection [RFC2782]
60 MDNS_RECORDTYPE_SRV = 33
61};
62
63enum mdns_entry_type {
64 MDNS_ENTRYTYPE_QUESTION = 0,
65 MDNS_ENTRYTYPE_ANSWER = 1,
66 MDNS_ENTRYTYPE_AUTHORITY = 2,
67 MDNS_ENTRYTYPE_ADDITIONAL = 3
68};
69
70enum mdns_class { MDNS_CLASS_IN = 1 };
71
72typedef enum mdns_record_type mdns_record_type_t;
73typedef enum mdns_entry_type mdns_entry_type_t;
74typedef enum mdns_class mdns_class_t;
75
76typedef int (*mdns_record_callback_fn)(int sock, const struct sockaddr* from, size_t addrlen,
77 mdns_entry_type_t entry, uint16_t transaction_id,
78 uint16_t rtype, uint16_t rclass, uint32_t ttl,
79 const void* data, size_t size, size_t offset, size_t length,
80 void* user_data);
81
82typedef struct mdns_string_t mdns_string_t;
83typedef struct mdns_string_pair_t mdns_string_pair_t;
84typedef struct mdns_record_srv_t mdns_record_srv_t;
85typedef struct mdns_record_txt_t mdns_record_txt_t;
86
87#ifdef _WIN32
88typedef int mdns_size_t;
89#else
90typedef size_t mdns_size_t;
91#endif
92
93struct mdns_string_t {
94 const char* str;
95 size_t length;
96};
97
98struct mdns_string_pair_t {
99 size_t offset;
100 size_t length;
101 int ref;
102};
103
104struct mdns_record_srv_t {
105 uint16_t priority;
106 uint16_t weight;
107 uint16_t port;
108 mdns_string_t name;
109};
110
111struct mdns_record_txt_t {
112 mdns_string_t key;
113 mdns_string_t value;
114};
115
116struct mdns_header_t {
117 uint16_t transaction_id;
118 uint16_t flags;
119 uint16_t questions;
120 uint16_t answer_rrs;
121 uint16_t authority_rrs;
122 uint16_t additional_rrs;
123};
124
125// mDNS/DNS-SD public API
126
128// pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY.
129// To send discovery requests and queries set 0 as port to assign a random user level port (also
130// done if passing a null saddr). To run discovery service listening for incoming
131// discoveries and queries, set MDNS_PORT as port.
132static int
133mdns_socket_open_ipv4(struct sockaddr_in* saddr);
134
136// pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY.
137// To send discovery requests and queries set 0 as port to assign a random user level port (also
138// done if passing a null saddr). To run discovery service listening for incoming
139// discoveries and queries, set MDNS_PORT as port.
140static int
141mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr);
142
144// pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any.
145// To send discovery requests and queries set 0 as port to assign a random user level port (also
146// done if passing a null saddr). To run discovery service listening for incoming
147// discoveries and queries, set MDNS_PORT as port.
148static int
149mdns_socket_open_ipv6(struct sockaddr_in6* saddr);
150
152// pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any.
153// To send discovery requests and queries set 0 as port to assign a random user level port (also
154// done if passing a null saddr). To run discovery service listening for incoming
155// discoveries and queries, set MDNS_PORT as port.
156static int
157mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr);
158
160static void
161mdns_socket_close(int sock);
162
164// opened on port MDNS_PORT using one of the mdns open or setup socket functions. Returns the
165// number of queries parsed.
166static size_t
167mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
168 void* user_data);
169
171// 0 on success, or <0 if error.
172static int
173mdns_discovery_send(int sock);
174
176// the given callback for parsing. Returns the number of responses parsed.
177static size_t
178mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
179 void* user_data);
180
182// or <0 if error.
183static int
184mdns_discovery_answer(int sock, const void* address, size_t address_size, void* buffer,
185 size_t capacity, const char* record, size_t length);
186
188// will be used to build the query packet. Returns the transaction ID for the query sent (always >0),
189// or <0 if error.
190static int
191mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer,
192 size_t capacity);
193
195// out any responses not matching the given transaction ID. Set the transaction ID to 0 to parse
196// all responses, even if it is not matching any sent query. Any data will be piped to the given
197// callback for parsing. Returns the number of responses parsed.
198static size_t
199mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
200 void* user_data, int only_last_query);
201
203// success, or <0 if error.
204static int
205mdns_query_answer(int sock, const void* address, size_t address_size, void* buffer, size_t capacity,
206 uint16_t transaction_id, const char* service, size_t service_length,
207 const char* hostname, size_t hostname_length, uint32_t ipv4, const uint8_t* ipv6,
208 uint16_t port, const char* txt, size_t txt_length);
209
210// Internal functions
211
212static mdns_string_t
213mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity);
214
215static int
216mdns_string_skip(const void* buffer, size_t size, size_t* offset);
217
218static int
219mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs,
220 size_t size_rhs, size_t* ofs_rhs);
221
222static void*
223mdns_string_make(void* data, size_t capacity, const char* name, size_t length);
224
225static void*
226mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset);
227
228static void*
229mdns_string_make_with_ref(void* data, size_t capacity, const char* name, size_t length,
230 size_t ref_offset);
231
232static mdns_string_t
233mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length,
234 char* strbuffer, size_t capacity);
235
236static mdns_record_srv_t
237mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length,
238 char* strbuffer, size_t capacity);
239
240static struct sockaddr_in*
241mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length,
242 struct sockaddr_in* addr);
243
244static struct sockaddr_in6*
245mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length,
246 struct sockaddr_in6* addr);
247
248static size_t
249mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length,
250 mdns_record_txt_t* records, size_t capacity);
251
252// Implementations
253
254static int
255mdns_socket_open_ipv4(struct sockaddr_in* saddr) {
256 int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
257 if (sock < 0)
258 return -1;
259 if (mdns_socket_setup_ipv4(sock, saddr)) {
260 mdns_socket_close(sock);
261 return -1;
262 }
263 return sock;
264}
265
266static int
267mdns_socket_setup_ipv4(int sock, struct sockaddr_in* saddr) {
268 unsigned char ttl = 1;
269 unsigned char loopback = 1;
270 unsigned int reuseaddr = 1;
271 struct ip_mreq req;
272
273 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr));
274#ifdef SO_REUSEPORT
275 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr));
276#endif
277 setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl));
278 setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback));
279
280 memset(&req, 0, sizeof(req));
281 req.imr_multiaddr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U));
282 req.imr_interface.s_addr = INADDR_ANY;
283 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof(req)))
284 return -1;
285
286 struct sockaddr_in sock_addr;
287 if (!saddr) {
288 saddr = &sock_addr;
289 memset(saddr, 0, sizeof(struct sockaddr_in));
290 saddr->sin_family = AF_INET;
291 saddr->sin_addr.s_addr = INADDR_ANY;
292#ifdef __APPLE__
293 saddr->sin_len = sizeof(struct sockaddr_in);
294#endif
295 }
296
297 if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in)))
298 return -1;
299
300#ifdef _WIN32
301 unsigned long param = 1;
302 ioctlsocket(sock, FIONBIO, &param);
303#else
304 const int flags = fcntl(sock, F_GETFL, 0);
305 fcntl(sock, F_SETFL, flags | O_NONBLOCK);
306#endif
307
308 return 0;
309}
310
311static int
312mdns_socket_open_ipv6(struct sockaddr_in6* saddr) {
313 int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
314 if (sock < 0)
315 return -1;
316 if (mdns_socket_setup_ipv6(sock, saddr)) {
317 mdns_socket_close(sock);
318 return -1;
319 }
320 return sock;
321}
322
323static int
324mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr) {
325 int hops = 1;
326 unsigned int loopback = 1;
327 unsigned int reuseaddr = 1;
328 struct ipv6_mreq req;
329
330 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr));
331#ifdef SO_REUSEPORT
332 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr));
333#endif
334 setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops));
335 setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback));
336
337 memset(&req, 0, sizeof(req));
338 req.ipv6mr_multiaddr.s6_addr[0] = 0xFF;
339 req.ipv6mr_multiaddr.s6_addr[1] = 0x02;
340 req.ipv6mr_multiaddr.s6_addr[15] = 0xFB;
341 if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req)))
342 return -1;
343
344 struct sockaddr_in6 sock_addr;
345 if (!saddr) {
346 saddr = &sock_addr;
347 memset(saddr, 0, sizeof(struct sockaddr_in6));
348 saddr->sin6_family = AF_INET6;
349 saddr->sin6_addr = in6addr_any;
350#ifdef __APPLE__
351 saddr->sin6_len = sizeof(struct sockaddr_in6);
352#endif
353 }
354
355 if (bind(sock, (struct sockaddr*)saddr, sizeof(struct sockaddr_in6)))
356 return -1;
357
358#ifdef _WIN32
359 unsigned long param = 1;
360 ioctlsocket(sock, FIONBIO, &param);
361#else
362 const int flags = fcntl(sock, F_GETFL, 0);
363 fcntl(sock, F_SETFL, flags | O_NONBLOCK);
364#endif
365
366 return 0;
367}
368
369static void
370mdns_socket_close(int sock) {
371#ifdef _WIN32
372 closesocket(sock);
373#else
374 close(sock);
375#endif
376}
377
378static int
379mdns_is_string_ref(uint8_t val) {
380 return (0xC0 == (val & 0xC0));
381}
382
383static mdns_string_pair_t
384mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) {
385 const uint8_t* buffer = (const uint8_t*)rawdata;
386 mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0};
387 if (!buffer[offset]) {
388 pair.offset = offset;
389 return pair;
390 }
391 if (mdns_is_string_ref(buffer[offset])) {
392 if (size < offset + 2)
393 return pair;
394
395 offset = 0x3fff & ntohs(*(uint16_t*)MDNS_POINTER_OFFSET(buffer, offset));
396 if (offset >= size)
397 return pair;
398
399 pair.ref = 1;
400 }
401
402 size_t length = (size_t)buffer[offset++];
403 if (size < offset + length)
404 return pair;
405
406 pair.offset = offset;
407 pair.length = length;
408
409 return pair;
410}
411
412static int
413mdns_string_skip(const void* buffer, size_t size, size_t* offset) {
414 size_t cur = *offset;
415 mdns_string_pair_t substr;
416 do {
417 substr = mdns_get_next_substring(buffer, size, cur);
418 if (substr.offset == MDNS_INVALID_POS)
419 return 0;
420 if (substr.ref) {
421 *offset = cur + 2;
422 return 1;
423 }
424 cur = substr.offset + substr.length;
425 } while (substr.length);
426
427 *offset = cur + 1;
428 return 1;
429}
430
431static int
432mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs,
433 size_t size_rhs, size_t* ofs_rhs) {
434 size_t lhs_cur = *ofs_lhs;
435 size_t rhs_cur = *ofs_rhs;
436 size_t lhs_end = MDNS_INVALID_POS;
437 size_t rhs_end = MDNS_INVALID_POS;
438 mdns_string_pair_t lhs_substr;
439 mdns_string_pair_t rhs_substr;
440 do {
441 lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur);
442 rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur);
443 if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS))
444 return 0;
445 if (lhs_substr.length != rhs_substr.length)
446 return 0;
447 if (strncasecmp((const char*)buffer_rhs + rhs_substr.offset,
448 (const char*)buffer_lhs + lhs_substr.offset, rhs_substr.length))
449 return 0;
450 if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS))
451 lhs_end = lhs_cur + 2;
452 if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS))
453 rhs_end = rhs_cur + 2;
454 lhs_cur = lhs_substr.offset + lhs_substr.length;
455 rhs_cur = rhs_substr.offset + rhs_substr.length;
456 } while (lhs_substr.length);
457
458 if (lhs_end == MDNS_INVALID_POS)
459 lhs_end = lhs_cur + 1;
460 *ofs_lhs = lhs_end;
461
462 if (rhs_end == MDNS_INVALID_POS)
463 rhs_end = rhs_cur + 1;
464 *ofs_rhs = rhs_end;
465
466 return 1;
467}
468
469static mdns_string_t
470mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity) {
471 size_t cur = *offset;
472 size_t end = MDNS_INVALID_POS;
473 mdns_string_pair_t substr;
474 mdns_string_t result;
475 result.str = str;
476 result.length = 0;
477 char* dst = str;
478 size_t remain = capacity;
479 do {
480 substr = mdns_get_next_substring(buffer, size, cur);
481 if (substr.offset == MDNS_INVALID_POS)
482 return result;
483 if (substr.ref && (end == MDNS_INVALID_POS))
484 end = cur + 2;
485 if (substr.length) {
486 size_t to_copy = (substr.length < remain) ? substr.length : remain;
487 memcpy(dst, (const char*)buffer + substr.offset, to_copy);
488 dst += to_copy;
489 remain -= to_copy;
490 if (remain) {
491 *dst++ = '.';
492 --remain;
493 }
494 }
495 cur = substr.offset + substr.length;
496 } while (substr.length);
497
498 if (end == MDNS_INVALID_POS)
499 end = cur + 1;
500 *offset = end;
501
502 result.length = capacity - remain;
503 return result;
504}
505
506static size_t
507mdns_string_find(const char* str, size_t length, char c, size_t offset) {
508 const void* found;
509 if (offset >= length)
510 return MDNS_INVALID_POS;
511 found = memchr(str + offset, c, length - offset);
512 if (found)
513 return (size_t)((const char*)found - str);
514 return MDNS_INVALID_POS;
515}
516
517static void*
518mdns_string_make(void* data, size_t capacity, const char* name, size_t length) {
519 size_t pos = 0;
520 size_t last_pos = 0;
521 size_t remain = capacity;
522 unsigned char* dest = (unsigned char*)data;
523 while ((last_pos < length) &&
524 ((pos = mdns_string_find(name, length, '.', last_pos)) != MDNS_INVALID_POS)) {
525 size_t sublength = pos - last_pos;
526 if (sublength < remain) {
527 *dest = (unsigned char)sublength;
528 memcpy(dest + 1, name + last_pos, sublength);
529 dest += sublength + 1;
530 remain -= sublength + 1;
531 } else {
532 return 0;
533 }
534 last_pos = pos + 1;
535 }
536 if (last_pos < length) {
537 size_t sublength = length - last_pos;
538 if (sublength < remain) {
539 *dest = (unsigned char)sublength;
540 memcpy(dest + 1, name + last_pos, sublength);
541 dest += sublength + 1;
542 remain -= sublength + 1;
543 } else {
544 return 0;
545 }
546 }
547 if (!remain)
548 return 0;
549 *dest++ = 0;
550 return dest;
551}
552
553static void*
554mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) {
555 if (capacity < 2)
556 return 0;
557 uint16_t* udata = (uint16_t*)data;
558 *udata++ = htons(0xC000 | (uint16_t)ref_offset);
559 return udata;
560}
561
562static void*
563mdns_string_make_with_ref(void* data, size_t capacity, const char* name, size_t length,
564 size_t ref_offset) {
565 void* remaindata = mdns_string_make(data, capacity, name, length);
566 capacity -= MDNS_POINTER_DIFF(remaindata, data);
567 if (!data || !capacity)
568 return 0;
569 return mdns_string_make_ref(MDNS_POINTER_OFFSET(remaindata, -1), capacity + 1, ref_offset);
570}
571
572static size_t
573mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const void* buffer,
574 size_t size, size_t* offset, mdns_entry_type_t type, uint16_t transaction_id,
575 size_t records, mdns_record_callback_fn callback, void* user_data) {
576 size_t parsed = 0;
577 int do_callback = (callback ? 1 : 0);
578 for (size_t i = 0; i < records; ++i) {
579 mdns_string_skip(buffer, size, offset);
580 const uint16_t* data = (const uint16_t*)((const char*)buffer + (*offset));
581
582 uint16_t rtype = ntohs(*data++);
583 uint16_t rclass = ntohs(*data++);
584 uint32_t ttl = ntohl(*(const uint32_t*)(const void*)data);
585 data += 2;
586 uint16_t length = ntohs(*data++);
587
588 *offset += 10;
589
590 if (do_callback) {
591 ++parsed;
592 if (callback(sock, from, addrlen, type, transaction_id, rtype, rclass, ttl, buffer,
593 size, *offset, length, user_data))
594 do_callback = 0;
595 }
596
597 *offset += length;
598 }
599 return parsed;
600}
601
602static int
603mdns_unicast_send(int sock, const void* address, size_t address_size, const void* buffer,
604 size_t size) {
605 if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address,
606 (socklen_t)address_size) < 0)
607 return -1;
608 return 0;
609}
610
611static int
612mdns_multicast_send(int sock, const void* buffer, size_t size) {
613 struct sockaddr_storage addr_storage;
614 struct sockaddr_in addr;
615 struct sockaddr_in6 addr6;
616 struct sockaddr* saddr = (struct sockaddr*)&addr_storage;
617 socklen_t saddrlen = sizeof(struct sockaddr_storage);
618 if (getsockname(sock, saddr, &saddrlen))
619 return -1;
620 if (saddr->sa_family == AF_INET6) {
621 memset(&addr6, 0, sizeof(struct sockaddr_in6));
622 addr6.sin6_family = AF_INET6;
623#ifdef __APPLE__
624 addr6.sin6_len = sizeof(struct sockaddr_in6);
625#endif
626 addr6.sin6_addr.s6_addr[0] = 0xFF;
627 addr6.sin6_addr.s6_addr[1] = 0x02;
628 addr6.sin6_addr.s6_addr[15] = 0xFB;
629 addr6.sin6_port = htons((unsigned short)MDNS_PORT);
630 saddr = (struct sockaddr*)&addr6;
631 saddrlen = sizeof(struct sockaddr_in6);
632 } else {
633 memset(&addr, 0, sizeof(struct sockaddr_in));
634 addr.sin_family = AF_INET;
635#ifdef __APPLE__
636 addr.sin_len = sizeof(struct sockaddr_in);
637#endif
638 addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U));
639 addr.sin_port = htons((unsigned short)MDNS_PORT);
640 saddr = (struct sockaddr*)&addr;
641 saddrlen = sizeof(struct sockaddr_in);
642 }
643
644 if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0)
645 return -1;
646 return 0;
647}
648
649static const uint8_t mdns_services_query[] = {
650 // Transaction ID
651 0x00, 0x00,
652 // Flags
653 0x00, 0x00,
654 // 1 question
655 0x00, 0x01,
656 // No answer, authority or additional RRs
657 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
658 // _services._dns-sd._udp.local.
659 0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', 0x07, '_', 'd', 'n', 's', '-', 's', 'd',
660 0x04, '_', 'u', 'd', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
661 // PTR record
662 0x00, MDNS_RECORDTYPE_PTR,
663 // QU (unicast response) and class IN
664 0x80, MDNS_CLASS_IN};
665
666static int
667mdns_discovery_send(int sock) {
668 return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query));
669}
670
671static size_t
672mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
673 void* user_data) {
674 struct sockaddr_in6 addr;
675 struct sockaddr* saddr = (struct sockaddr*)&addr;
676 socklen_t addrlen = sizeof(addr);
677 memset(&addr, 0, sizeof(addr));
678#ifdef __APPLE__
679 saddr->sa_len = sizeof(addr);
680#endif
681 int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
682 if (ret <= 0)
683 return 0;
684
685 size_t data_size = (size_t)ret;
686 size_t records = 0;
687 uint16_t* data = (uint16_t*)buffer;
688
689 uint16_t transaction_id = ntohs(*data++);
690 uint16_t flags = ntohs(*data++);
691 uint16_t questions = ntohs(*data++);
692 uint16_t answer_rrs = ntohs(*data++);
693 uint16_t authority_rrs = ntohs(*data++);
694 uint16_t additional_rrs = ntohs(*data++);
695
696 if (transaction_id || (flags != 0x8400))
697 return 0; // Not a reply to our question
698
699 if (questions != 1)
700 return 0;
701
702 int i;
703 for (i = 0; i < questions; ++i) {
704 size_t ofs = (size_t)((char*)data - (char*)buffer);
705 size_t verify_ofs = 12;
706 // Verify it's our question, _services._dns-sd._udp.local.
707 if (!mdns_string_equal(buffer, data_size, &ofs, mdns_services_query,
708 sizeof(mdns_services_query), &verify_ofs))
709 return 0;
710 data = (uint16_t*)((char*)buffer + ofs);
711
712 uint16_t rtype = ntohs(*data++);
713 uint16_t rclass = ntohs(*data++);
714
715 // Make sure we get a reply based on our PTR question for class IN
716 if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN))
717 return 0;
718 }
719
720 int do_callback = 1;
721 for (i = 0; i < answer_rrs; ++i) {
722 size_t ofs = (size_t)((char*)data - (char*)buffer);
723 size_t verify_ofs = 12;
724 // Verify it's an answer to our question, _services._dns-sd._udp.local.
725 int is_answer = mdns_string_equal(buffer, data_size, &ofs, mdns_services_query,
726 sizeof(mdns_services_query), &verify_ofs);
727 data = (uint16_t*)((char*)buffer + ofs);
728
729 uint16_t rtype = ntohs(*data++);
730 uint16_t rclass = ntohs(*data++);
731 uint32_t ttl = ntohl(*(uint32_t*)(void*)data);
732 data += 2;
733 uint16_t length = ntohs(*data++);
734 if (length >= (data_size - ofs))
735 return 0;
736
737 if (is_answer && do_callback) {
738 ++records;
739 if (callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, transaction_id, rtype, rclass,
740 ttl, buffer, data_size, (size_t)((char*)data - (char*)buffer), length,
741 user_data))
742 do_callback = 0;
743 }
744 data = (uint16_t*)((char*)data + length);
745 }
746
747 size_t offset = (size_t)((char*)data - (char*)buffer);
748 records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
749 MDNS_ENTRYTYPE_AUTHORITY, transaction_id, authority_rrs, callback,
750 user_data);
751 records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
752 MDNS_ENTRYTYPE_ADDITIONAL, transaction_id, additional_rrs,
753 callback, user_data);
754
755 return records;
756}
757
758static size_t
759mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
760 void* user_data) {
761 struct sockaddr_in6 addr;
762 struct sockaddr* saddr = (struct sockaddr*)&addr;
763 socklen_t addrlen = sizeof(addr);
764 memset(&addr, 0, sizeof(addr));
765#ifdef __APPLE__
766 saddr->sa_len = sizeof(addr);
767#endif
768 int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
769 if (ret <= 0)
770 return 0;
771
772 size_t data_size = (size_t)ret;
773 uint16_t* data = (uint16_t*)buffer;
774
775 uint16_t transaction_id = ntohs(*data++);
776 uint16_t flags = ntohs(*data++);
777 uint16_t questions = ntohs(*data++);
778 /*
779 This data is unused at the moment, skip
780 uint16_t answer_rrs = ntohs(*data++);
781 uint16_t authority_rrs = ntohs(*data++);
782 uint16_t additional_rrs = ntohs(*data++);
783 */
784 data += 3;
785
786 size_t parsed = 0;
787 for (int iquestion = 0; iquestion < questions; ++iquestion) {
788 size_t question_offset = (size_t)((char*)data - (char*)buffer);
789 size_t offset = question_offset;
790 size_t verify_ofs = 12;
791 if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
792 sizeof(mdns_services_query), &verify_ofs)) {
793 if (transaction_id || flags || (questions != 1))
794 return 0;
795 } else {
796 offset = question_offset;
797 if (!mdns_string_skip(buffer, data_size, &offset))
798 break;
799 }
800 size_t length = offset - question_offset;
801 data = (uint16_t*)((char*)buffer + offset);
802
803 uint16_t rtype = ntohs(*data++);
804 uint16_t rclass = ntohs(*data++);
805
806 // Make sure we get a PTR question of class IN
807 if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN))
808 return 0;
809
810 if (callback)
811 callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, transaction_id, rtype, rclass,
812 0, buffer, data_size, question_offset, length, user_data);
813
814 ++parsed;
815 }
816
817 return parsed;
818}
819
820static int
821mdns_discovery_answer(int sock, const void* address, size_t address_size, void* buffer,
822 size_t capacity, const char* record, size_t length) {
823 if (capacity < (sizeof(mdns_services_query) + 32 + length))
824 return -1;
825
826 uint16_t* data = (uint16_t*)buffer;
827 // Basic reply structure
828 memcpy(data, mdns_services_query, sizeof(mdns_services_query));
829 // Flags
830 uint16_t* flags = data + 1;
831 *flags = htons(0x8400);
832 // One answer
833 uint16_t* answers = data + 3;
834 *answers = htons(1);
835
836 // Fill in answer PTR record
837 data = (uint16_t*)((char*)buffer + sizeof(mdns_services_query));
838 // Reference _services._dns-sd._udp.local. string in question
839 *data++ = htons(0xC000 | 12);
840 // Type
841 *data++ = htons(MDNS_RECORDTYPE_PTR);
842 // Rclass
843 *data++ = htons(MDNS_CLASS_IN);
844 // TTL
845 *(uint32_t*)data = htonl(10);
846 data += 2;
847 // Record string length
848 uint16_t* record_length = data++;
849 uint8_t* record_data = (uint8_t*)data;
850 size_t remain = capacity - (sizeof(mdns_services_query) + 10);
851 record_data = (uint8_t*)mdns_string_make(record_data, remain, record, length);
852 *record_length = htons((uint16_t)(record_data - (uint8_t*)data));
853 *record_data++ = 0;
854
855 ptrdiff_t tosend = (char*)record_data - (char*)buffer;
856 return mdns_unicast_send(sock, address, address_size, buffer, (size_t)tosend);
857}
858
859static uint16_t mdns_transaction_id = 0;
860
861static int
862mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer,
863 size_t capacity) {
864 if (capacity < (17 + length))
865 return -1;
866
867 uint16_t transaction_id = ++mdns_transaction_id;
868 if (!transaction_id)
869 transaction_id = ++mdns_transaction_id;
870 uint16_t* data = (uint16_t*)buffer;
871 // Transaction ID
872 *data++ = htons(transaction_id);
873 // Flags
874 *data++ = 0;
875 // Questions
876 *data++ = htons(1);
877 // No answer, authority or additional RRs
878 *data++ = 0;
879 *data++ = 0;
880 *data++ = 0;
881 // Name string
882 data = (uint16_t*)mdns_string_make(data, capacity - 17, name, length);
883 if (!data)
884 return -1;
885 // Record type
886 *data++ = htons(type);
888 *data++ = htons(0x8000U | MDNS_CLASS_IN);
889
890 ptrdiff_t tosend = (char*)data - (char*)buffer;
891 if (mdns_multicast_send(sock, buffer, (size_t)tosend))
892 return -1;
893 return transaction_id;
894}
895
896static size_t
897mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
898 void* user_data, int only_transaction_id) {
899 struct sockaddr_in6 addr;
900 struct sockaddr* saddr = (struct sockaddr*)&addr;
901 socklen_t addrlen = sizeof(addr);
902 memset(&addr, 0, sizeof(addr));
903#ifdef __APPLE__
904 saddr->sa_len = sizeof(addr);
905#endif
906 int ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
907 if (ret <= 0)
908 return 0;
909
910 size_t data_size = (size_t)ret;
911 uint16_t* data = (uint16_t*)buffer;
912
913 uint16_t transaction_id = ntohs(*data++);
914 ++data; // uint16_t flags = ntohs(*data++);
915 uint16_t questions = ntohs(*data++);
916 uint16_t answer_rrs = ntohs(*data++);
917 uint16_t authority_rrs = ntohs(*data++);
918 uint16_t additional_rrs = ntohs(*data++);
919
920 if ((only_transaction_id > 0) && (transaction_id != only_transaction_id)) // || (flags != 0x8400))
921 return 0; // Not a reply to the wanted query
922
923 if (questions > 1)
924 return 0;
925
926 // Skip questions part
927 int i;
928 for (i = 0; i < questions; ++i) {
929 size_t ofs = (size_t)((char*)data - (char*)buffer);
930 if (!mdns_string_skip(buffer, data_size, &ofs))
931 return 0;
932 data = (uint16_t*)((char*)buffer + ofs);
933 ++data;
934 ++data;
935 }
936
937 size_t records = 0;
938 size_t offset = MDNS_POINTER_DIFF(data, buffer);
939 records +=
940 mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, MDNS_ENTRYTYPE_ANSWER,
941 transaction_id, answer_rrs, callback, user_data);
942 records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
943 MDNS_ENTRYTYPE_AUTHORITY, transaction_id, authority_rrs, callback,
944 user_data);
945 records += mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
946 MDNS_ENTRYTYPE_ADDITIONAL, transaction_id, additional_rrs,
947 callback, user_data);
948 return records;
949}
950
951static int
952mdns_query_answer(int sock, const void* address, size_t address_size, void* buffer, size_t capacity,
953 uint16_t transaction_id, const char* service, size_t service_length,
954 const char* hostname, size_t hostname_length, uint32_t ipv4, const uint8_t* ipv6,
955 uint16_t port, const char* txt, size_t txt_length) {
956 if (capacity < (sizeof(struct mdns_header_t) + 32 + service_length + hostname_length))
957 return -1;
958
959 int use_ipv4 = (ipv4 != 0);
960 int use_ipv6 = (ipv6 != 0);
961 int use_txt = (txt && txt_length && (txt_length <= 255));
962
963 // Basic answer structure
964 struct mdns_header_t* header = (struct mdns_header_t*)buffer;
965 header->transaction_id = htons(transaction_id);
966 header->flags = htons(0x8400);
967 header->questions = htons(1);
968 header->answer_rrs = htons(2 + (u_short)use_ipv4 + (u_short)use_ipv6 + (u_short)use_txt);
969 header->authority_rrs = 0;
970 header->additional_rrs = 0;
971
972 // Fill in question
973 void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t));
974 size_t service_offset = MDNS_POINTER_DIFF(data, buffer);
975 size_t remain = capacity - service_offset;
976 data = mdns_string_make(data, remain, service, service_length);
977 size_t local_offset = MDNS_POINTER_DIFF(data, buffer) - 7;
978 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
979 if (!data || (remain <= 4))
980 return -1;
981
982 uint16_t* udata = (uint16_t*)data;
983 *udata++ = htons(MDNS_RECORDTYPE_PTR);
984 *udata++ = htons(MDNS_CLASS_IN);
985 data = udata;
986 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
987
988 // Fill in answers
989 // PTR record for service
990 data = mdns_string_make_ref(data, remain, service_offset);
991 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
992 if (!data || (remain <= 10))
993 return -1;
994 udata = (uint16_t*)data;
995 *udata++ = htons(MDNS_RECORDTYPE_PTR); // type
996 *udata++ = htons(MDNS_CLASS_IN); // rclass
997 *(uint32_t*)udata = htonl(10); // ttl
998 udata += 2;
999 uint16_t* record_length = udata++; // length
1000 data = udata;
1001 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1002 data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, service_offset);
1003 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1004 if (!data || (remain <= 10))
1005 return -1;
1006 *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1));
1007
1008 // SRV record
1009 data = mdns_string_make_ref(data, remain, service_offset);
1010 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1011 if (!data || (remain <= 10))
1012 return -1;
1013 udata = (uint16_t*)data;
1014 *udata++ = htons(MDNS_RECORDTYPE_SRV); // type
1015 *udata++ = htons(MDNS_CLASS_IN); // rclass
1016 *(uint32_t*)udata = htonl(10); // ttl
1017 udata += 2;
1018 record_length = udata++; // length
1019 *udata++ = htons(0); // priority
1020 *udata++ = htons(0); // weight
1021 *udata++ = htons(port); // port
1022 // Make a string <hostname>.local.
1023 data = udata;
1024 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1025 data = mdns_string_make_with_ref(data, remain, hostname, hostname_length, local_offset);
1026 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1027 if (!data || (remain <= 10))
1028 return -1;
1029 *record_length = htons((uint16_t)MDNS_POINTER_DIFF(data, record_length + 1));
1030
1031 // A record
1032 if (use_ipv4) {
1033 data = mdns_string_make_ref(data, remain, service_offset);
1034 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1035 if (!data || (remain <= 14))
1036 return -1;
1037 udata = (uint16_t*)data;
1038 *udata++ = htons(MDNS_RECORDTYPE_A); // type
1039 *udata++ = htons(MDNS_CLASS_IN); // rclass
1040 *(uint32_t*)udata = htonl(10); // ttl
1041 udata += 2;
1042 *udata++ = htons(4); // length
1043 *(uint32_t*)udata = ipv4; // ipv4 address
1044 udata += 2;
1045 data = udata;
1046 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1047 }
1048
1049 // AAAA record
1050 if (use_ipv6) {
1051 data = mdns_string_make_ref(data, remain, service_offset);
1052 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1053 if (!data || (remain <= 26))
1054 return -1;
1055 udata = (uint16_t*)data;
1056 *udata++ = htons(MDNS_RECORDTYPE_AAAA); // type
1057 *udata++ = htons(MDNS_CLASS_IN); // rclass
1058 *(uint32_t*)udata = htonl(10); // ttl
1059 udata += 2;
1060 *udata++ = htons(16); // length
1061 memcpy(udata, ipv6, 16); // ipv6 address
1062 data = MDNS_POINTER_OFFSET(udata, 16);
1063 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1064 }
1065
1066 // TXT record
1067 if (use_txt) {
1068 data = mdns_string_make_ref(data, remain, service_offset);
1069 remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1070 if (!data || (remain <= (11 + txt_length)))
1071 return -1;
1072 udata = (uint16_t*)data;
1073 *udata++ = htons(MDNS_RECORDTYPE_TXT); // type
1074 *udata++ = htons(MDNS_CLASS_IN); // rclass
1075 *(uint32_t*)udata = htonl(10); // ttl
1076 udata += 2;
1077 *udata++ = htons((unsigned short)(txt_length + 1)); // length
1078 char* txt_record = (char*)udata;
1079 *txt_record++ = (char)txt_length;
1080 memcpy(txt_record, txt, txt_length); // txt record
1081 data = MDNS_POINTER_OFFSET(txt_record, txt_length);
1082 //Unused until multiple txt records are supported
1083 //remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1084 }
1085
1086 size_t tosend = MDNS_POINTER_DIFF(data, buffer);
1087 return mdns_unicast_send(sock, address, address_size, buffer, tosend);
1088}
1089
1090static mdns_string_t
1091mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length,
1092 char* strbuffer, size_t capacity) {
1093 // PTR record is just a string
1094 if ((size >= offset + length) && (length >= 2))
1095 return mdns_string_extract(buffer, size, &offset, strbuffer, capacity);
1096 mdns_string_t empty = {0, 0};
1097 return empty;
1098}
1099
1100static mdns_record_srv_t
1101mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length,
1102 char* strbuffer, size_t capacity) {
1103 mdns_record_srv_t srv;
1104 memset(&srv, 0, sizeof(mdns_record_srv_t));
1105 // Read the priority, weight, port number and the discovery name
1106 // SRV record format (http://www.ietf.org/rfc/rfc2782.txt):
1107 // 2 bytes network-order unsigned priority
1108 // 2 bytes network-order unsigned weight
1109 // 2 bytes network-order unsigned port
1110 // string: discovery (domain) name, minimum 2 bytes when compressed
1111 if ((size >= offset + length) && (length >= 8)) {
1112 const uint16_t* recorddata = (const uint16_t*)((const char*)buffer + offset);
1113 srv.priority = ntohs(*recorddata++);
1114 srv.weight = ntohs(*recorddata++);
1115 srv.port = ntohs(*recorddata++);
1116 offset += 6;
1117 srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity);
1118 }
1119 return srv;
1120}
1121
1122static struct sockaddr_in*
1123mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length,
1124 struct sockaddr_in* addr) {
1125 memset(addr, 0, sizeof(struct sockaddr_in));
1126 addr->sin_family = AF_INET;
1127#ifdef __APPLE__
1128 addr->sin_len = sizeof(struct sockaddr_in);
1129#endif
1130 if ((size >= offset + length) && (length == 4))
1131 addr->sin_addr.s_addr = *(const uint32_t*)((const char*)buffer + offset);
1132 return addr;
1133}
1134
1135static struct sockaddr_in6*
1136mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length,
1137 struct sockaddr_in6* addr) {
1138 memset(addr, 0, sizeof(struct sockaddr_in6));
1139 addr->sin6_family = AF_INET6;
1140#ifdef __APPLE__
1141 addr->sin6_len = sizeof(struct sockaddr_in6);
1142#endif
1143 if ((size >= offset + length) && (length == 16))
1144 addr->sin6_addr = *(const struct in6_addr*)((const char*)buffer + offset);
1145 return addr;
1146}
1147
1148static size_t
1149mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length,
1150 mdns_record_txt_t* records, size_t capacity) {
1151 size_t parsed = 0;
1152 const char* strdata;
1153 size_t separator, sublength;
1154 size_t end = offset + length;
1155
1156 if (size < end)
1157 end = size;
1158
1159 while ((offset < end) && (parsed < capacity)) {
1160 strdata = (const char*)buffer + offset;
1161 sublength = *(const unsigned char*)strdata;
1162
1163 ++strdata;
1164 offset += sublength + 1;
1165
1166 separator = 0;
1167 for (size_t c = 0; c < sublength; ++c) {
1168 // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E]
1169 if ((strdata[c] < 0x20) || (strdata[c] > 0x7E))
1170 break;
1171 if (strdata[c] == '=') {
1172 separator = c;
1173 break;
1174 }
1175 }
1176
1177 if (!separator)
1178 continue;
1179
1180 if (separator < sublength) {
1181 records[parsed].key.str = strdata;
1182 records[parsed].key.length = separator;
1183 records[parsed].value.str = strdata + separator + 1;
1184 records[parsed].value.length = sublength - (separator + 1);
1185 } else {
1186 records[parsed].key.str = strdata;
1187 records[parsed].key.length = sublength;
1188 }
1189
1190 ++parsed;
1191 }
1192
1193 return parsed;
1194}
1195
1196#ifdef _WIN32
1197#undef strncasecmp
1198#endif
1199
1200#ifdef __cplusplus
1201}
1202#endif