libiio  0.21
Library for interfacing with IIO devices
/home/travis/build/analogdevicesinc/libiio/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
35 extern "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 
49 enum 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 
63 enum 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 
70 enum mdns_class { MDNS_CLASS_IN = 1 };
71 
72 typedef enum mdns_record_type mdns_record_type_t;
73 typedef enum mdns_entry_type mdns_entry_type_t;
74 typedef enum mdns_class mdns_class_t;
75 
76 typedef 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 
82 typedef struct mdns_string_t mdns_string_t;
83 typedef struct mdns_string_pair_t mdns_string_pair_t;
84 typedef struct mdns_record_srv_t mdns_record_srv_t;
85 typedef struct mdns_record_txt_t mdns_record_txt_t;
86 
87 #ifdef _WIN32
88 typedef int mdns_size_t;
89 #else
90 typedef size_t mdns_size_t;
91 #endif
92 
93 struct mdns_string_t {
94  const char* str;
95  size_t length;
96 };
97 
98 struct mdns_string_pair_t {
99  size_t offset;
100  size_t length;
101  int ref;
102 };
103 
104 struct mdns_record_srv_t {
105  uint16_t priority;
106  uint16_t weight;
107  uint16_t port;
108  mdns_string_t name;
109 };
110 
111 struct mdns_record_txt_t {
112  mdns_string_t key;
113  mdns_string_t value;
114 };
115 
116 struct 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.
132 static int
133 mdns_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.
140 static int
141 mdns_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.
148 static int
149 mdns_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.
156 static int
157 mdns_socket_setup_ipv6(int sock, struct sockaddr_in6* saddr);
158 
160 static void
161 mdns_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.
166 static size_t
167 mdns_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.
172 static int
173 mdns_discovery_send(int sock);
174 
176 // the given callback for parsing. Returns the number of responses parsed.
177 static size_t
178 mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
179  void* user_data);
180 
182 // or <0 if error.
183 static int
184 mdns_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.
190 static int
191 mdns_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.
198 static size_t
199 mdns_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.
204 static int
205 mdns_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 
212 static mdns_string_t
213 mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity);
214 
215 static int
216 mdns_string_skip(const void* buffer, size_t size, size_t* offset);
217 
218 static int
219 mdns_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 
222 static void*
223 mdns_string_make(void* data, size_t capacity, const char* name, size_t length);
224 
225 static void*
226 mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset);
227 
228 static void*
229 mdns_string_make_with_ref(void* data, size_t capacity, const char* name, size_t length,
230  size_t ref_offset);
231 
232 static mdns_string_t
233 mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length,
234  char* strbuffer, size_t capacity);
235 
236 static mdns_record_srv_t
237 mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length,
238  char* strbuffer, size_t capacity);
239 
240 static struct sockaddr_in*
241 mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length,
242  struct sockaddr_in* addr);
243 
244 static struct sockaddr_in6*
245 mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length,
246  struct sockaddr_in6* addr);
247 
248 static size_t
249 mdns_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 
254 static int
255 mdns_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 
266 static int
267 mdns_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 
311 static int
312 mdns_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 
323 static int
324 mdns_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 
369 static void
370 mdns_socket_close(int sock) {
371 #ifdef _WIN32
372  closesocket(sock);
373 #else
374  close(sock);
375 #endif
376 }
377 
378 static int
379 mdns_is_string_ref(uint8_t val) {
380  return (0xC0 == (val & 0xC0));
381 }
382 
383 static mdns_string_pair_t
384 mdns_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 
412 static int
413 mdns_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 
431 static int
432 mdns_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 
469 static mdns_string_t
470 mdns_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 
506 static size_t
507 mdns_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 
517 static void*
518 mdns_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 
553 static void*
554 mdns_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 
562 static void*
563 mdns_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 
572 static size_t
573 mdns_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 
602 static int
603 mdns_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 
611 static int
612 mdns_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 
649 static 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 
666 static int
667 mdns_discovery_send(int sock) {
668  return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query));
669 }
670 
671 static size_t
672 mdns_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 
758 static size_t
759 mdns_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 
820 static int
821 mdns_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 
859 static uint16_t mdns_transaction_id = 0;
860 
861 static int
862 mdns_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 
896 static size_t
897 mdns_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 
951 static int
952 mdns_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 
1090 static mdns_string_t
1091 mdns_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 
1100 static mdns_record_srv_t
1101 mdns_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 
1122 static struct sockaddr_in*
1123 mdns_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 
1135 static struct sockaddr_in6*
1136 mdns_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 
1148 static size_t
1149 mdns_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