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