_________ __                 __
        /   _____//  |_____________ _/  |______     ____  __ __  ______
        \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
        /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ \
       /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
               \/                  \/          \//_____/            \/
    ______________________                           ______________________
                          T H E   W A R   B E G I N S
                   Stratagus - A free fantasy real time strategy game engine

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