File indexing completed on 2024-05-12 04:58:52

0001 /* mdns.h  -  mDNS/DNS-SD library v1.4.3  -  Public Domain  -  2017 Mattias Jansson
0002  *
0003  * This library provides a cross-platform mDNS and DNS-SD library in C.
0004  * The implementation is based on RFC 6762 and RFC 6763.
0005  *
0006  * The latest source code is always available at
0007  *
0008  * https://github.com/mjansson/mdns
0009  *
0010  * This library is put in the public domain; you can redistribute it and/or modify it without any
0011  * restrictions.
0012  *
0013  */
0014 
0015 // clang-format off
0016 #pragma once
0017 
0018 #include <stdint.h>
0019 #include <stddef.h>
0020 #include <stdlib.h>
0021 #include <string.h>
0022 
0023 #include <fcntl.h>
0024 #ifdef _WIN32
0025 #include <Winsock2.h>
0026 #include <Ws2tcpip.h>
0027 #define strncasecmp _strnicmp
0028 #else
0029 #include <unistd.h>
0030 #include <sys/socket.h>
0031 #include <netinet/in.h>
0032 #endif
0033 
0034 #ifdef __cplusplus
0035 extern "C" {
0036 #endif
0037 
0038 #define MDNS_INVALID_POS ((size_t)-1)
0039 
0040 #define MDNS_STRING_CONST(s) (s), (sizeof((s)) - 1)
0041 #define MDNS_STRING_ARGS(s) s.str, s.length
0042 #define MDNS_STRING_FORMAT(s) (int)((s).length), s.str
0043 
0044 #define MDNS_POINTER_OFFSET(p, ofs) ((void*)((char*)(p) + (ptrdiff_t)(ofs)))
0045 #define MDNS_POINTER_OFFSET_CONST(p, ofs) ((const void*)((const char*)(p) + (ptrdiff_t)(ofs)))
0046 #define MDNS_POINTER_DIFF(a, b) ((size_t)((const char*)(a) - (const char*)(b)))
0047 
0048 #define MDNS_PORT 5353
0049 #define MDNS_UNICAST_RESPONSE 0x8000U
0050 #define MDNS_CACHE_FLUSH 0x8000U
0051 #define MDNS_MAX_SUBSTRINGS 64
0052 
0053 enum mdns_record_type {
0054     MDNS_RECORDTYPE_IGNORE = 0,
0055     // Address
0056     MDNS_RECORDTYPE_A = 1,
0057     // Domain Name pointer
0058     MDNS_RECORDTYPE_PTR = 12,
0059     // Arbitrary text string
0060     MDNS_RECORDTYPE_TXT = 16,
0061     // IP6 Address [Thomson]
0062     MDNS_RECORDTYPE_AAAA = 28,
0063     // Server Selection [RFC2782]
0064     MDNS_RECORDTYPE_SRV = 33,
0065     // Any available records
0066     MDNS_RECORDTYPE_ANY = 255
0067 };
0068 
0069 enum mdns_entry_type {
0070     MDNS_ENTRYTYPE_QUESTION = 0,
0071     MDNS_ENTRYTYPE_ANSWER = 1,
0072     MDNS_ENTRYTYPE_AUTHORITY = 2,
0073     MDNS_ENTRYTYPE_ADDITIONAL = 3
0074 };
0075 
0076 enum mdns_class { MDNS_CLASS_IN = 1, MDNS_CLASS_ANY = 255 };
0077 
0078 typedef enum mdns_record_type mdns_record_type_t;
0079 typedef enum mdns_entry_type mdns_entry_type_t;
0080 typedef enum mdns_class mdns_class_t;
0081 
0082 typedef int (*mdns_record_callback_fn)(int sock, const struct sockaddr* from, size_t addrlen,
0083                                        mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype,
0084                                        uint16_t rclass, uint32_t ttl, const void* data, size_t size,
0085                                        size_t name_offset, size_t name_length, size_t record_offset,
0086                                        size_t record_length, void* user_data);
0087 
0088 typedef struct mdns_string_t mdns_string_t;
0089 typedef struct mdns_string_pair_t mdns_string_pair_t;
0090 typedef struct mdns_string_table_item_t mdns_string_table_item_t;
0091 typedef struct mdns_string_table_t mdns_string_table_t;
0092 typedef struct mdns_record_t mdns_record_t;
0093 typedef struct mdns_record_srv_t mdns_record_srv_t;
0094 typedef struct mdns_record_ptr_t mdns_record_ptr_t;
0095 typedef struct mdns_record_a_t mdns_record_a_t;
0096 typedef struct mdns_record_aaaa_t mdns_record_aaaa_t;
0097 typedef struct mdns_record_txt_t mdns_record_txt_t;
0098 typedef struct mdns_query_t mdns_query_t;
0099 
0100 #ifdef _WIN32
0101 typedef int mdns_size_t;
0102 typedef int mdns_ssize_t;
0103 #else
0104 typedef size_t mdns_size_t;
0105 typedef ssize_t mdns_ssize_t;
0106 #endif
0107 
0108 struct mdns_string_t {
0109     const char* str;
0110     size_t length;
0111 };
0112 
0113 struct mdns_string_pair_t {
0114     size_t offset;
0115     size_t length;
0116     int ref;
0117 };
0118 
0119 struct mdns_string_table_t {
0120     size_t offset[16];
0121     size_t count;
0122     size_t next;
0123 };
0124 
0125 struct mdns_record_srv_t {
0126     uint16_t priority;
0127     uint16_t weight;
0128     uint16_t port;
0129     mdns_string_t name;
0130 };
0131 
0132 struct mdns_record_ptr_t {
0133     mdns_string_t name;
0134 };
0135 
0136 struct mdns_record_a_t {
0137     struct sockaddr_in addr;
0138 };
0139 
0140 struct mdns_record_aaaa_t {
0141     struct sockaddr_in6 addr;
0142 };
0143 
0144 struct mdns_record_txt_t {
0145     mdns_string_t key;
0146     mdns_string_t value;
0147 };
0148 
0149 struct mdns_record_t {
0150     mdns_string_t name;
0151     mdns_record_type_t type;
0152     union mdns_record_data {
0153         mdns_record_ptr_t ptr;
0154         mdns_record_srv_t srv;
0155         mdns_record_a_t a;
0156         mdns_record_aaaa_t aaaa;
0157         mdns_record_txt_t txt;
0158     } data;
0159     uint16_t rclass;
0160     uint32_t ttl;
0161 };
0162 
0163 struct mdns_header_t {
0164     uint16_t query_id;
0165     uint16_t flags;
0166     uint16_t questions;
0167     uint16_t answer_rrs;
0168     uint16_t authority_rrs;
0169     uint16_t additional_rrs;
0170 };
0171 
0172 struct mdns_query_t {
0173     mdns_record_type_t type;
0174     const char* name;
0175     size_t length;
0176 };
0177 
0178 // mDNS/DNS-SD public API
0179 
0180 //! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass
0181 //! in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. To
0182 //! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a
0183 //! random user level ephemeral port. To run discovery service listening for incoming discoveries
0184 //! and queries, you must set MDNS_PORT as port.
0185 static inline int
0186 mdns_socket_open_ipv4(const struct sockaddr_in* saddr);
0187 
0188 //! Setup an already opened IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface,
0189 //! pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY.
0190 //! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a
0191 //! random user level ephemeral port. To run discovery service listening for incoming discoveries
0192 //! and queries, you must set MDNS_PORT as port.
0193 static inline int
0194 mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr);
0195 
0196 //! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass
0197 //! in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. To
0198 //! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a
0199 //! random user level ephemeral port. To run discovery service listening for incoming discoveries
0200 //! and queries, you must set MDNS_PORT as port.
0201 static inline int
0202 mdns_socket_open_ipv6(const struct sockaddr_in6* saddr);
0203 
0204 //! Setup an already opened IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface,
0205 //! pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any.
0206 //! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a
0207 //! random user level ephemeral port. To run discovery service listening for incoming discoveries
0208 //! and queries, you must set MDNS_PORT as port.
0209 static inline int
0210 mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr);
0211 
0212 //! Close a socket opened with mdns_socket_open_ipv4 and mdns_socket_open_ipv6.
0213 static inline void
0214 mdns_socket_close(int sock);
0215 
0216 //! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been opened
0217 //! on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be 32 bit
0218 //! aligned. Parsing is stopped when callback function returns non-zero. Returns the number of
0219 //! queries parsed.
0220 static inline size_t
0221 mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
0222                    void* user_data);
0223 
0224 //! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns 0
0225 //! on success, or <0 if error.
0226 static inline int
0227 mdns_discovery_send(int sock);
0228 
0229 //! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to
0230 //! the given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback
0231 //! function returns non-zero. Returns the number of responses parsed.
0232 static inline size_t
0233 mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
0234                     void* user_data);
0235 
0236 //! Send a multicast mDNS query on the given socket for the given service name. The supplied buffer
0237 //! will be used to build the query packet and must be 32 bit aligned. The query ID can be set to
0238 //! non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for
0239 //! multicast queries. The query will request a unicast response if the socket is bound to an
0240 //! ephemeral port, or a multicast response if the socket is bound to mDNS port 5353. Returns the
0241 //! used query ID, or <0 if error.
0242 static inline int
0243 mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer,
0244                 size_t capacity, uint16_t query_id);
0245 
0246 //! Send a multicast mDNS query on the given socket for the given service names. The supplied buffer
0247 //! will be used to build the query packet and must be 32 bit aligned. The query ID can be set to
0248 //! non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for
0249 //! multicast queries. Each additional service name query consists of a triplet - a record type
0250 //! (mdns_record_type_t), a name string pointer (const char*) and a name length (size_t). The list
0251 //! of variable arguments should be terminated with a record type of 0. The query will request a
0252 //! unicast response if the socket is bound to an ephemeral port, or a multicast response if the
0253 //! socket is bound to mDNS port 5353. Returns the used query ID, or <0 if error.
0254 static inline int
0255 mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer,
0256                      size_t capacity, uint16_t query_id);
0257 
0258 //! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering
0259 //! out any responses not matching the given query ID. Set the query ID to 0 to parse all responses,
0260 //! even if it is not matching the query ID set in a specific query. Any data will be piped to the
0261 //! given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback
0262 //! function returns non-zero. Returns the number of responses parsed.
0263 static inline size_t
0264 mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
0265                 void* user_data, int query_id);
0266 
0267 //! Send a variable unicast mDNS query answer to any question with variable number of records to the
0268 //! given address. Use the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query
0269 //! recieved to determine if the answer should be sent unicast (bit set) or multicast (bit not set).
0270 //! Buffer must be 32 bit aligned. The record type and name should match the data from the query
0271 //! recieved. Returns 0 if success, or <0 if error.
0272 static inline int
0273 mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer,
0274                           size_t capacity, uint16_t query_id, mdns_record_type_t record_type,
0275                           const char* name, size_t name_length, mdns_record_t answer,
0276                           const mdns_record_t* authority, size_t authority_count,
0277                           const mdns_record_t* additional, size_t additional_count);
0278 
0279 //! Send a variable multicast mDNS query answer to any question with variable number of records. Use
0280 //! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine
0281 //! if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be 32 bit
0282 //! aligned. Returns 0 if success, or <0 if error.
0283 static inline int
0284 mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
0285                             const mdns_record_t* authority, size_t authority_count,
0286                             const mdns_record_t* additional, size_t additional_count);
0287 
0288 //! Send a variable multicast mDNS announcement (as an unsolicited answer) with variable number of
0289 //! records.Buffer must be 32 bit aligned. Returns 0 if success, or <0 if error. Use this on service
0290 //! startup to announce your instance to the local network.
0291 static inline int
0292 mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
0293                         const mdns_record_t* authority, size_t authority_count,
0294                         const mdns_record_t* additional, size_t additional_count);
0295 
0296 //! Send a variable multicast mDNS announcement. Use this on service end for removing the resource
0297 //! from the local network. The records must be identical to the according announcement.
0298 static inline int
0299 mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
0300                        const mdns_record_t* authority, size_t authority_count,
0301                        const mdns_record_t* additional, size_t additional_count);
0302 
0303 // Parse records functions
0304 
0305 //! Parse a PTR record, returns the name in the record
0306 static inline mdns_string_t
0307 mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length,
0308                       char* strbuffer, size_t capacity);
0309 
0310 //! Parse a SRV record, returns the priority, weight, port and name in the record
0311 static inline mdns_record_srv_t
0312 mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length,
0313                       char* strbuffer, size_t capacity);
0314 
0315 //! Parse an A record, returns the IPv4 address in the record
0316 static inline struct sockaddr_in*
0317 mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length,
0318                     struct sockaddr_in* addr);
0319 
0320 //! Parse an AAAA record, returns the IPv6 address in the record
0321 static inline struct sockaddr_in6*
0322 mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length,
0323                        struct sockaddr_in6* addr);
0324 
0325 //! Parse a TXT record, returns the number of key=value records parsed and stores the key-value
0326 //! pairs in the supplied buffer
0327 static inline size_t
0328 mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length,
0329                       mdns_record_txt_t* records, size_t capacity);
0330 
0331 // Internal functions
0332 
0333 static inline mdns_string_t
0334 mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity);
0335 
0336 static inline int
0337 mdns_string_skip(const void* buffer, size_t size, size_t* offset);
0338 
0339 static inline size_t
0340 mdns_string_find(const char* str, size_t length, char c, size_t offset);
0341 
0342 //! Compare if two strings are equal. If the strings are equal it returns >0 and the offset variables are
0343 //! updated to the end of the corresponding strings. If the strings are not equal it returns 0 and
0344 //! the offset variables are NOT updated.
0345 static inline int
0346 mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs,
0347                   size_t size_rhs, size_t* ofs_rhs);
0348 
0349 static inline void*
0350 mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length,
0351                  mdns_string_table_t* string_table);
0352 
0353 static inline size_t
0354 mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity,
0355                        const char* str, size_t first_length, size_t total_length);
0356 
0357 // Implementations
0358 
0359 static inline uint16_t
0360 mdns_ntohs(const void* data) {
0361     uint16_t aligned;
0362     memcpy(&aligned, data, sizeof(uint16_t));
0363     return ntohs(aligned);
0364 }
0365 
0366 static inline uint32_t
0367 mdns_ntohl(const void* data) {
0368     uint32_t aligned;
0369     memcpy(&aligned, data, sizeof(uint32_t));
0370     return ntohl(aligned);
0371 }
0372 
0373 static inline void*
0374 mdns_htons(void* data, uint16_t val) {
0375     val = htons(val);
0376     memcpy(data, &val, sizeof(uint16_t));
0377     return MDNS_POINTER_OFFSET(data, sizeof(uint16_t));
0378 }
0379 
0380 static inline void*
0381 mdns_htonl(void* data, uint32_t val) {
0382     val = htonl(val);
0383     memcpy(data, &val, sizeof(uint32_t));
0384     return MDNS_POINTER_OFFSET(data, sizeof(uint32_t));
0385 }
0386 
0387 static inline int
0388 mdns_socket_open_ipv4(const struct sockaddr_in* saddr) {
0389     int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
0390     if (sock < 0)
0391         return -1;
0392     if (mdns_socket_setup_ipv4(sock, saddr)) {
0393         mdns_socket_close(sock);
0394         return -1;
0395     }
0396     return sock;
0397 }
0398 
0399 static inline int
0400 mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) {
0401     unsigned char ttl = 1;
0402     unsigned char loopback = 1;
0403     unsigned int reuseaddr = 1;
0404     struct ip_mreq req;
0405 
0406     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr));
0407 #ifdef SO_REUSEPORT
0408     setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr));
0409 #endif
0410     setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl));
0411     setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback));
0412 
0413     memset(&req, 0, sizeof(req));
0414     req.imr_multiaddr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U));
0415     if (saddr)
0416         req.imr_interface = saddr->sin_addr;
0417     if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof(req)))
0418         return -1;
0419 
0420     struct sockaddr_in sock_addr;
0421     if (!saddr) {
0422         memset(&sock_addr, 0, sizeof(struct sockaddr_in));
0423         sock_addr.sin_family = AF_INET;
0424         sock_addr.sin_addr.s_addr = INADDR_ANY;
0425 #ifdef __APPLE__
0426         sock_addr.sin_len = sizeof(struct sockaddr_in);
0427 #endif
0428     } else {
0429         memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in));
0430         setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&sock_addr.sin_addr,
0431                    sizeof(sock_addr.sin_addr));
0432 #ifndef _WIN32
0433         sock_addr.sin_addr.s_addr = INADDR_ANY;
0434 #endif
0435     }
0436 
0437     if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in)))
0438         return -1;
0439 
0440 #ifdef _WIN32
0441     unsigned long param = 1;
0442     ioctlsocket(sock, FIONBIO, &param);
0443 #else
0444     const int flags = fcntl(sock, F_GETFL, 0);
0445     fcntl(sock, F_SETFL, flags | O_NONBLOCK);
0446 #endif
0447 
0448     return 0;
0449 }
0450 
0451 static inline int
0452 mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) {
0453     int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
0454     if (sock < 0)
0455         return -1;
0456     if (mdns_socket_setup_ipv6(sock, saddr)) {
0457         mdns_socket_close(sock);
0458         return -1;
0459     }
0460     return sock;
0461 }
0462 
0463 static inline int
0464 mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) {
0465     int hops = 1;
0466     unsigned int loopback = 1;
0467     unsigned int reuseaddr = 1;
0468     struct ipv6_mreq req;
0469 
0470     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr));
0471 #ifdef SO_REUSEPORT
0472     setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr));
0473 #endif
0474     setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops));
0475     setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback));
0476 
0477     memset(&req, 0, sizeof(req));
0478     req.ipv6mr_multiaddr.s6_addr[0] = 0xFF;
0479     req.ipv6mr_multiaddr.s6_addr[1] = 0x02;
0480     req.ipv6mr_multiaddr.s6_addr[15] = 0xFB;
0481     if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req)))
0482         return -1;
0483 
0484     struct sockaddr_in6 sock_addr;
0485     if (!saddr) {
0486         memset(&sock_addr, 0, sizeof(struct sockaddr_in6));
0487         sock_addr.sin6_family = AF_INET6;
0488         sock_addr.sin6_addr = in6addr_any;
0489 #ifdef __APPLE__
0490         sock_addr.sin6_len = sizeof(struct sockaddr_in6);
0491 #endif
0492     } else {
0493         memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in6));
0494         unsigned int ifindex = 0;
0495         setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&ifindex, sizeof(ifindex));
0496 #ifndef _WIN32
0497         sock_addr.sin6_addr = in6addr_any;
0498 #endif
0499     }
0500 
0501     if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in6)))
0502         return -1;
0503 
0504 #ifdef _WIN32
0505     unsigned long param = 1;
0506     ioctlsocket(sock, FIONBIO, &param);
0507 #else
0508     const int flags = fcntl(sock, F_GETFL, 0);
0509     fcntl(sock, F_SETFL, flags | O_NONBLOCK);
0510 #endif
0511 
0512     return 0;
0513 }
0514 
0515 static inline void
0516 mdns_socket_close(int sock) {
0517 #ifdef _WIN32
0518     closesocket(sock);
0519 #else
0520     close(sock);
0521 #endif
0522 }
0523 
0524 static inline int
0525 mdns_is_string_ref(uint8_t val) {
0526     return (0xC0 == (val & 0xC0));
0527 }
0528 
0529 static inline mdns_string_pair_t
0530 mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) {
0531     const uint8_t* buffer = (const uint8_t*)rawdata;
0532     mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0};
0533     if (offset >= size)
0534         return pair;
0535     if (!buffer[offset]) {
0536         pair.offset = offset;
0537         return pair;
0538     }
0539     int recursion = 0;
0540     while (mdns_is_string_ref(buffer[offset])) {
0541         if (size < offset + 2)
0542             return pair;
0543 
0544         offset = mdns_ntohs(MDNS_POINTER_OFFSET(buffer, offset)) & 0x3fff;
0545         if (offset >= size)
0546             return pair;
0547 
0548         pair.ref = 1;
0549         if (++recursion > 16)
0550             return pair;
0551     }
0552 
0553     size_t length = (size_t)buffer[offset++];
0554     if (size < offset + length)
0555         return pair;
0556 
0557     pair.offset = offset;
0558     pair.length = length;
0559 
0560     return pair;
0561 }
0562 
0563 static inline int
0564 mdns_string_skip(const void* buffer, size_t size, size_t* offset) {
0565     size_t cur = *offset;
0566     mdns_string_pair_t substr;
0567     unsigned int counter = 0;
0568     do {
0569         substr = mdns_get_next_substring(buffer, size, cur);
0570         if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS))
0571             return 0;
0572         if (substr.ref) {
0573             *offset = cur + 2;
0574             return 1;
0575         }
0576         cur = substr.offset + substr.length;
0577     } while (substr.length);
0578 
0579     *offset = cur + 1;
0580     return 1;
0581 }
0582 
0583 static inline int
0584 mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs,
0585                   size_t size_rhs, size_t* ofs_rhs) {
0586     size_t lhs_cur = *ofs_lhs;
0587     size_t rhs_cur = *ofs_rhs;
0588     size_t lhs_end = MDNS_INVALID_POS;
0589     size_t rhs_end = MDNS_INVALID_POS;
0590     mdns_string_pair_t lhs_substr;
0591     mdns_string_pair_t rhs_substr;
0592     unsigned int counter = 0;
0593     do {
0594         lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur);
0595         rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur);
0596         if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS) ||
0597             (counter++ > MDNS_MAX_SUBSTRINGS))
0598             return 0;
0599         if (lhs_substr.length != rhs_substr.length)
0600             return 0;
0601         if (strncasecmp((const char*)MDNS_POINTER_OFFSET_CONST(buffer_rhs, rhs_substr.offset),
0602                         (const char*)MDNS_POINTER_OFFSET_CONST(buffer_lhs, lhs_substr.offset),
0603                         rhs_substr.length))
0604             return 0;
0605         if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS))
0606             lhs_end = lhs_cur + 2;
0607         if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS))
0608             rhs_end = rhs_cur + 2;
0609         lhs_cur = lhs_substr.offset + lhs_substr.length;
0610         rhs_cur = rhs_substr.offset + rhs_substr.length;
0611     } while (lhs_substr.length);
0612 
0613     if (lhs_end == MDNS_INVALID_POS)
0614         lhs_end = lhs_cur + 1;
0615     *ofs_lhs = lhs_end;
0616 
0617     if (rhs_end == MDNS_INVALID_POS)
0618         rhs_end = rhs_cur + 1;
0619     *ofs_rhs = rhs_end;
0620 
0621     return 1;
0622 }
0623 
0624 static inline mdns_string_t
0625 mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity) {
0626     size_t cur = *offset;
0627     size_t end = MDNS_INVALID_POS;
0628     mdns_string_pair_t substr;
0629     mdns_string_t result;
0630     result.str = str;
0631     result.length = 0;
0632     char* dst = str;
0633     unsigned int counter = 0;
0634     size_t remain = capacity;
0635     do {
0636         substr = mdns_get_next_substring(buffer, size, cur);
0637         if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS))
0638             return result;
0639         if (substr.ref && (end == MDNS_INVALID_POS))
0640             end = cur + 2;
0641         if (substr.length) {
0642             size_t to_copy = (substr.length < remain) ? substr.length : remain;
0643             memcpy(dst, (const char*)buffer + substr.offset, to_copy);
0644             dst += to_copy;
0645             remain -= to_copy;
0646             if (remain) {
0647                 *dst++ = '.';
0648                 --remain;
0649             }
0650         }
0651         cur = substr.offset + substr.length;
0652     } while (substr.length);
0653 
0654     if (end == MDNS_INVALID_POS)
0655         end = cur + 1;
0656     *offset = end;
0657 
0658     result.length = capacity - remain;
0659     return result;
0660 }
0661 
0662 static inline size_t
0663 mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity,
0664                        const char* str, size_t first_length, size_t total_length) {
0665     if (!string_table)
0666         return MDNS_INVALID_POS;
0667 
0668     for (size_t istr = 0; istr < string_table->count; ++istr) {
0669         if (string_table->offset[istr] >= capacity)
0670             continue;
0671         size_t offset = 0;
0672         mdns_string_pair_t sub_string =
0673             mdns_get_next_substring(buffer, capacity, string_table->offset[istr]);
0674         if (!sub_string.length || (sub_string.length != first_length))
0675             continue;
0676         if (memcmp(str, MDNS_POINTER_OFFSET(buffer, sub_string.offset), sub_string.length))
0677             continue;
0678 
0679         // Initial substring matches, now match all remaining substrings
0680         offset += first_length + 1;
0681         while (offset < total_length) {
0682             size_t dot_pos = mdns_string_find(str, total_length, '.', offset);
0683             if (dot_pos == MDNS_INVALID_POS)
0684                 dot_pos = total_length;
0685             size_t current_length = dot_pos - offset;
0686 
0687             sub_string =
0688                 mdns_get_next_substring(buffer, capacity, sub_string.offset + sub_string.length);
0689             if (!sub_string.length || (sub_string.length != current_length))
0690                 break;
0691             if (memcmp(str + offset, MDNS_POINTER_OFFSET(buffer, sub_string.offset),
0692                        sub_string.length))
0693                 break;
0694 
0695             offset = dot_pos + 1;
0696         }
0697 
0698         // Return reference offset if entire string matches
0699         if (offset >= total_length)
0700             return string_table->offset[istr];
0701     }
0702 
0703     return MDNS_INVALID_POS;
0704 }
0705 
0706 static inline void
0707 mdns_string_table_add(mdns_string_table_t* string_table, size_t offset) {
0708     if (!string_table)
0709         return;
0710 
0711     string_table->offset[string_table->next] = offset;
0712 
0713     size_t table_capacity = sizeof(string_table->offset) / sizeof(string_table->offset[0]);
0714     if (++string_table->count > table_capacity)
0715         string_table->count = table_capacity;
0716     if (++string_table->next >= table_capacity)
0717         string_table->next = 0;
0718 }
0719 
0720 static inline size_t
0721 mdns_string_find(const char* str, size_t length, char c, size_t offset) {
0722     const void* found;
0723     if (offset >= length)
0724         return MDNS_INVALID_POS;
0725     found = memchr(str + offset, c, length - offset);
0726     if (found)
0727         return (size_t)MDNS_POINTER_DIFF(found, str);
0728     return MDNS_INVALID_POS;
0729 }
0730 
0731 static inline void*
0732 mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) {
0733     if (capacity < 2)
0734         return 0;
0735     return mdns_htons(data, 0xC000 | (uint16_t)ref_offset);
0736 }
0737 
0738 static inline void*
0739 mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length,
0740                  mdns_string_table_t* string_table) {
0741     size_t last_pos = 0;
0742     size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer);
0743     if (name[length - 1] == '.')
0744         --length;
0745     while (last_pos < length) {
0746         size_t pos = mdns_string_find(name, length, '.', last_pos);
0747         size_t sub_length = ((pos != MDNS_INVALID_POS) ? pos : length) - last_pos;
0748         size_t total_length = length - last_pos;
0749 
0750         size_t ref_offset =
0751             mdns_string_table_find(string_table, buffer, capacity,
0752                                    (char*)MDNS_POINTER_OFFSET(name, last_pos), sub_length,
0753                                    total_length);
0754         if (ref_offset != MDNS_INVALID_POS)
0755             return mdns_string_make_ref(data, remain, ref_offset);
0756 
0757         if (remain <= (sub_length + 1))
0758             return 0;
0759 
0760         *(unsigned char*)data = (unsigned char)sub_length;
0761         memcpy(MDNS_POINTER_OFFSET(data, 1), name + last_pos, sub_length);
0762         mdns_string_table_add(string_table, MDNS_POINTER_DIFF(data, buffer));
0763 
0764         data = MDNS_POINTER_OFFSET(data, sub_length + 1);
0765         last_pos = ((pos != MDNS_INVALID_POS) ? pos + 1 : length);
0766         remain = capacity - MDNS_POINTER_DIFF(data, buffer);
0767     }
0768 
0769     if (!remain)
0770         return 0;
0771 
0772     *(unsigned char*)data = 0;
0773     return MDNS_POINTER_OFFSET(data, 1);
0774 }
0775 
0776 static inline size_t
0777 mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const void* buffer,
0778                    size_t size, size_t* offset, mdns_entry_type_t type, uint16_t query_id,
0779                    size_t records, mdns_record_callback_fn callback, void* user_data) {
0780     size_t parsed = 0;
0781     for (size_t i = 0; i < records; ++i) {
0782         size_t name_offset = *offset;
0783         mdns_string_skip(buffer, size, offset);
0784         if (((*offset) + 10) > size)
0785             return parsed;
0786         size_t name_length = (*offset) - name_offset;
0787         const uint16_t* data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, *offset);
0788 
0789         uint16_t rtype = mdns_ntohs(data++);
0790         uint16_t rclass = mdns_ntohs(data++);
0791         uint32_t ttl = mdns_ntohl(data);
0792         data += 2;
0793         uint16_t length = mdns_ntohs(data++);
0794 
0795         *offset += 10;
0796 
0797         if (length <= (size - (*offset))) {
0798             ++parsed;
0799             if (callback &&
0800                 callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size,
0801                          name_offset, name_length, *offset, length, user_data))
0802                 break;
0803         }
0804 
0805         *offset += length;
0806     }
0807     return parsed;
0808 }
0809 
0810 static inline int
0811 mdns_unicast_send(int sock, const void* address, size_t address_size, const void* buffer,
0812                   size_t size) {
0813     if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address,
0814                (socklen_t)address_size) < 0)
0815         return -1;
0816     return 0;
0817 }
0818 
0819 static inline int
0820 mdns_multicast_send(int sock, const void* buffer, size_t size) {
0821     struct sockaddr_storage addr_storage;
0822     struct sockaddr_in addr;
0823     struct sockaddr_in6 addr6;
0824     struct sockaddr* saddr = (struct sockaddr*)&addr_storage;
0825     socklen_t saddrlen = sizeof(struct sockaddr_storage);
0826     if (getsockname(sock, saddr, &saddrlen))
0827         return -1;
0828     if (saddr->sa_family == AF_INET6) {
0829         memset(&addr6, 0, sizeof(addr6));
0830         addr6.sin6_family = AF_INET6;
0831 #ifdef __APPLE__
0832         addr6.sin6_len = sizeof(addr6);
0833 #endif
0834         addr6.sin6_addr.s6_addr[0] = 0xFF;
0835         addr6.sin6_addr.s6_addr[1] = 0x02;
0836         addr6.sin6_addr.s6_addr[15] = 0xFB;
0837         addr6.sin6_port = htons((unsigned short)MDNS_PORT);
0838         saddr = (struct sockaddr*)&addr6;
0839         saddrlen = sizeof(addr6);
0840     } else {
0841         memset(&addr, 0, sizeof(addr));
0842         addr.sin_family = AF_INET;
0843 #ifdef __APPLE__
0844         addr.sin_len = sizeof(addr);
0845 #endif
0846         addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U));
0847         addr.sin_port = htons((unsigned short)MDNS_PORT);
0848         saddr = (struct sockaddr*)&addr;
0849         saddrlen = sizeof(addr);
0850     }
0851 
0852     if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0)
0853         return -1;
0854     return 0;
0855 }
0856 
0857 static const uint8_t mdns_services_query[] = {
0858     // Query ID
0859     0x00, 0x00,
0860     // Flags
0861     0x00, 0x00,
0862     // 1 question
0863     0x00, 0x01,
0864     // No answer, authority or additional RRs
0865     0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0866     // _services._dns-sd._udp.local.
0867     0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', 0x07, '_', 'd', 'n', 's', '-', 's', 'd',
0868     0x04, '_', 'u', 'd', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
0869     // PTR record
0870     0x00, MDNS_RECORDTYPE_PTR,
0871     // QU (unicast response) and class IN
0872     0x80, MDNS_CLASS_IN};
0873 
0874 static inline int
0875 mdns_discovery_send(int sock) {
0876     return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query));
0877 }
0878 
0879 static inline size_t
0880 mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
0881                     void* user_data) {
0882     struct sockaddr_in6 addr;
0883     struct sockaddr* saddr = (struct sockaddr*)&addr;
0884     socklen_t addrlen = sizeof(addr);
0885     memset(&addr, 0, sizeof(addr));
0886 #ifdef __APPLE__
0887     saddr->sa_len = sizeof(addr);
0888 #endif
0889     mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
0890     if (ret <= 0)
0891         return 0;
0892 
0893     size_t data_size = (size_t)ret;
0894     size_t records = 0;
0895     const uint16_t* data = (uint16_t*)buffer;
0896 
0897     uint16_t query_id = mdns_ntohs(data++);
0898     uint16_t flags = mdns_ntohs(data++);
0899     uint16_t questions = mdns_ntohs(data++);
0900     uint16_t answer_rrs = mdns_ntohs(data++);
0901     uint16_t authority_rrs = mdns_ntohs(data++);
0902     uint16_t additional_rrs = mdns_ntohs(data++);
0903 
0904     // According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case)
0905     if (query_id || (flags != 0x8400))
0906         return 0;  // Not a reply to our question
0907 
0908     // It seems some implementations do not fill the correct questions field,
0909     // so ignore this check for now and only validate answer string
0910     // if (questions != 1)
0911     //  return 0;
0912 
0913     int i;
0914     for (i = 0; i < questions; ++i) {
0915         size_t offset = MDNS_POINTER_DIFF(data, buffer);
0916         size_t verify_offset = 12;
0917         // Verify it's our question, _services._dns-sd._udp.local.
0918         if (!mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
0919                                sizeof(mdns_services_query), &verify_offset))
0920             return 0;
0921         data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset);
0922 
0923         uint16_t rtype = mdns_ntohs(data++);
0924         uint16_t rclass = mdns_ntohs(data++);
0925 
0926         // Make sure we get a reply based on our PTR question for class IN
0927         if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN))
0928             return 0;
0929     }
0930 
0931     for (i = 0; i < answer_rrs; ++i) {
0932         size_t offset = MDNS_POINTER_DIFF(data, buffer);
0933         size_t verify_offset = 12;
0934         // Verify it's an answer to our question, _services._dns-sd._udp.local.
0935         size_t name_offset = offset;
0936         int is_answer = mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
0937                                           sizeof(mdns_services_query), &verify_offset);
0938         if (!is_answer && !mdns_string_skip(buffer, data_size, &offset))
0939             break;
0940         size_t name_length = offset - name_offset;
0941         if ((offset + 10) > data_size)
0942             return records;
0943         data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset);
0944 
0945         uint16_t rtype = mdns_ntohs(data++);
0946         uint16_t rclass = mdns_ntohs(data++);
0947         uint32_t ttl = mdns_ntohl(data);
0948         data += 2;
0949         uint16_t length = mdns_ntohs(data++);
0950         if (length > (data_size - offset))
0951             return 0;
0952 
0953         if (is_answer) {
0954             ++records;
0955             offset = MDNS_POINTER_DIFF(data, buffer);
0956             if (callback &&
0957                 callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl,
0958                          buffer, data_size, name_offset, name_length, offset, length, user_data))
0959                 return records;
0960         }
0961         data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length);
0962     }
0963 
0964     size_t total_records = records;
0965     size_t offset = MDNS_POINTER_DIFF(data, buffer);
0966     records =
0967         mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
0968                            MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data);
0969     total_records += records;
0970     if (records != authority_rrs)
0971         return total_records;
0972 
0973     records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
0974                                  MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback,
0975                                  user_data);
0976     total_records += records;
0977     if (records != additional_rrs)
0978         return total_records;
0979 
0980     return total_records;
0981 }
0982 
0983 static inline size_t
0984 mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
0985                    void* user_data) {
0986     struct sockaddr_in6 addr;
0987     struct sockaddr* saddr = (struct sockaddr*)&addr;
0988     socklen_t addrlen = sizeof(addr);
0989     memset(&addr, 0, sizeof(addr));
0990 #ifdef __APPLE__
0991     saddr->sa_len = sizeof(addr);
0992 #endif
0993     mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
0994     if (ret <= 0)
0995         return 0;
0996 
0997     size_t data_size = (size_t)ret;
0998     const uint16_t* data = (const uint16_t*)buffer;
0999 
1000     uint16_t query_id = mdns_ntohs(data++);
1001     uint16_t flags = mdns_ntohs(data++);
1002     uint16_t questions = mdns_ntohs(data++);
1003     uint16_t answer_rrs = mdns_ntohs(data++);
1004     uint16_t authority_rrs = mdns_ntohs(data++);
1005     uint16_t additional_rrs = mdns_ntohs(data++);
1006 
1007     size_t records;
1008     size_t total_records = 0;
1009     for (int iquestion = 0; iquestion < questions; ++iquestion) {
1010         size_t question_offset = MDNS_POINTER_DIFF(data, buffer);
1011         size_t offset = question_offset;
1012         size_t verify_offset = 12;
1013         int dns_sd = 0;
1014         if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
1015                               sizeof(mdns_services_query), &verify_offset)) {
1016             dns_sd = 1;
1017         } else if (!mdns_string_skip(buffer, data_size, &offset)) {
1018             break;
1019         }
1020         size_t length = offset - question_offset;
1021         data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset);
1022 
1023         uint16_t rtype = mdns_ntohs(data++);
1024         uint16_t rclass = mdns_ntohs(data++);
1025         uint16_t class_without_flushbit = rclass & ~MDNS_CACHE_FLUSH;
1026 
1027         // Make sure we get a question of class IN or ANY
1028         if (!((class_without_flushbit == MDNS_CLASS_IN) ||
1029               (class_without_flushbit == MDNS_CLASS_ANY))) {
1030             break;
1031         }
1032 
1033         if (dns_sd && flags)
1034             continue;
1035 
1036         ++total_records;
1037         if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype,
1038                                  rclass, 0, buffer, data_size, question_offset, length,
1039                                  question_offset, length, user_data))
1040             return total_records;
1041     }
1042 
1043     size_t offset = MDNS_POINTER_DIFF(data, buffer);
1044     records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
1045                                  MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data);
1046     total_records += records;
1047     if (records != answer_rrs)
1048         return total_records;
1049 
1050     records =
1051         mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
1052                            MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data);
1053     total_records += records;
1054     if (records != authority_rrs)
1055         return total_records;
1056 
1057     records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
1058                                  MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback,
1059                                  user_data);
1060 
1061     return total_records;
1062 }
1063 
1064 static inline int
1065 mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer,
1066                 size_t capacity, uint16_t query_id) {
1067     mdns_query_t query;
1068     query.type = type;
1069     query.name = name;
1070     query.length = length;
1071     return mdns_multiquery_send(sock, &query, 1, buffer, capacity, query_id);
1072 }
1073 
1074 static inline int
1075 mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, size_t capacity,
1076                      uint16_t query_id) {
1077     if (!count || (capacity < (sizeof(struct mdns_header_t) + (6 * count))))
1078         return -1;
1079 
1080     // Ask for a unicast response since it's a one-shot query
1081     uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE;
1082 
1083     struct sockaddr_storage addr_storage;
1084     struct sockaddr* saddr = (struct sockaddr*)&addr_storage;
1085     socklen_t saddrlen = sizeof(addr_storage);
1086     if (getsockname(sock, saddr, &saddrlen) == 0) {
1087         if ((saddr->sa_family == AF_INET) &&
1088             (ntohs(((struct sockaddr_in*)saddr)->sin_port) == MDNS_PORT))
1089             rclass &= ~MDNS_UNICAST_RESPONSE;
1090         else if ((saddr->sa_family == AF_INET6) &&
1091                  (ntohs(((struct sockaddr_in6*)saddr)->sin6_port) == MDNS_PORT))
1092             rclass &= ~MDNS_UNICAST_RESPONSE;
1093     }
1094 
1095     struct mdns_header_t* header = (struct mdns_header_t*)buffer;
1096     // Query ID
1097     header->query_id = htons((unsigned short)query_id);
1098     // Flags
1099     header->flags = 0;
1100     // Questions
1101     header->questions = htons((unsigned short)count);
1102     // No answer, authority or additional RRs
1103     header->answer_rrs = 0;
1104     header->authority_rrs = 0;
1105     header->additional_rrs = 0;
1106     // Fill in questions
1107     void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t));
1108     for (size_t iq = 0; iq < count; ++iq) {
1109         // Name string
1110         data = mdns_string_make(buffer, capacity, data, query[iq].name, query[iq].length, 0);
1111         if (!data)
1112             return -1;
1113         // Record type
1114         data = mdns_htons(data, query[iq].type);
1115         //! Optional unicast response based on local port, class IN
1116         data = mdns_htons(data, rclass);
1117     }
1118 
1119     size_t tosend = MDNS_POINTER_DIFF(data, buffer);
1120     if (mdns_multicast_send(sock, buffer, (size_t)tosend))
1121         return -1;
1122     return query_id;
1123 }
1124 
1125 static inline size_t
1126 mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback,
1127                 void* user_data, int only_query_id) {
1128     struct sockaddr_in6 addr;
1129     struct sockaddr* saddr = (struct sockaddr*)&addr;
1130     socklen_t addrlen = sizeof(addr);
1131     memset(&addr, 0, sizeof(addr));
1132 #ifdef __APPLE__
1133     saddr->sa_len = sizeof(addr);
1134 #endif
1135     mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
1136     if (ret <= 0)
1137         return 0;
1138 
1139     size_t data_size = (size_t)ret;
1140     const uint16_t* data = (const uint16_t*)buffer;
1141 
1142     uint16_t query_id = mdns_ntohs(data++);
1143     uint16_t flags = mdns_ntohs(data++);
1144     uint16_t questions = mdns_ntohs(data++);
1145     uint16_t answer_rrs = mdns_ntohs(data++);
1146     uint16_t authority_rrs = mdns_ntohs(data++);
1147     uint16_t additional_rrs = mdns_ntohs(data++);
1148     (void)sizeof(flags);
1149 
1150     if ((only_query_id > 0) && (query_id != only_query_id))
1151         return 0;  // Not a reply to the wanted one-shot query
1152 
1153     // Skip questions part
1154     int i;
1155     for (i = 0; i < questions; ++i) {
1156         size_t offset = MDNS_POINTER_DIFF(data, buffer);
1157         if (!mdns_string_skip(buffer, data_size, &offset))
1158             return 0;
1159         data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset);
1160         // Record type and class not used, skip
1161         // uint16_t rtype = mdns_ntohs(data++);
1162         // uint16_t rclass = mdns_ntohs(data++);
1163         data += 2;
1164     }
1165 
1166     size_t records = 0;
1167     size_t total_records = 0;
1168     size_t offset = MDNS_POINTER_DIFF(data, buffer);
1169     records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
1170                                  MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data);
1171     total_records += records;
1172     if (records != answer_rrs)
1173         return total_records;
1174 
1175     records =
1176         mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
1177                            MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data);
1178     total_records += records;
1179     if (records != authority_rrs)
1180         return total_records;
1181 
1182     records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset,
1183                                  MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback,
1184                                  user_data);
1185     total_records += records;
1186     if (records != additional_rrs)
1187         return total_records;
1188 
1189     return total_records;
1190 }
1191 
1192 static inline void*
1193 mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data,
1194                                  mdns_record_type_t record_type, const char* name,
1195                                  size_t name_length, mdns_string_table_t* string_table) {
1196     data = mdns_string_make(buffer, capacity, data, name, name_length, string_table);
1197     if (!data)
1198         return 0;
1199     size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1200     if (remain < 4)
1201         return 0;
1202 
1203     data = mdns_htons(data, record_type);
1204     data = mdns_htons(data, MDNS_UNICAST_RESPONSE | MDNS_CLASS_IN);
1205 
1206     return data;
1207 }
1208 
1209 static inline void*
1210 mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_record_t record,
1211                               mdns_string_table_t* string_table) {
1212     data = mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table);
1213     if (!data)
1214         return 0;
1215     size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1216     if (remain < 10)
1217         return 0;
1218 
1219     data = mdns_htons(data, record.type);
1220     data = mdns_htons(data, record.rclass);
1221     data = mdns_htonl(data, record.ttl);
1222     data = mdns_htons(data, 0);  // Length, to be filled later
1223     return data;
1224 }
1225 
1226 static inline void*
1227 mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t record,
1228                        mdns_string_table_t* string_table) {
1229     // TXT records will be coalesced into one record later
1230     if (!data || (record.type == MDNS_RECORDTYPE_TXT))
1231         return data;
1232 
1233     data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table);
1234     if (!data)
1235         return 0;
1236 
1237     // Pointer to length of record to be filled at end
1238     void* record_length = MDNS_POINTER_OFFSET(data, -2);
1239     void* record_data = data;
1240 
1241     size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1242     switch (record.type) {
1243         case MDNS_RECORDTYPE_PTR:
1244             data = mdns_string_make(buffer, capacity, data, record.data.ptr.name.str,
1245                                     record.data.ptr.name.length, string_table);
1246             break;
1247 
1248         case MDNS_RECORDTYPE_SRV:
1249             if (remain <= 6)
1250                 return 0;
1251             data = mdns_htons(data, record.data.srv.priority);
1252             data = mdns_htons(data, record.data.srv.weight);
1253             data = mdns_htons(data, record.data.srv.port);
1254             data = mdns_string_make(buffer, capacity, data, record.data.srv.name.str,
1255                                     record.data.srv.name.length, string_table);
1256             break;
1257 
1258         case MDNS_RECORDTYPE_A:
1259             if (remain < 4)
1260                 return 0;
1261             memcpy(data, &record.data.a.addr.sin_addr.s_addr, 4);
1262             data = MDNS_POINTER_OFFSET(data, 4);
1263             break;
1264 
1265         case MDNS_RECORDTYPE_AAAA:
1266             if (remain < 16)
1267                 return 0;
1268             memcpy(data, &record.data.aaaa.addr.sin6_addr, 16);  // ipv6 address
1269             data = MDNS_POINTER_OFFSET(data, 16);
1270             break;
1271 
1272         default:
1273             break;
1274     }
1275 
1276     if (!data)
1277         return 0;
1278 
1279     // Fill record length
1280     mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data));
1281     return data;
1282 }
1283 
1284 static inline void
1285 mdns_record_update_rclass_ttl(mdns_record_t* record, uint16_t rclass, uint32_t ttl) {
1286     if (!record->rclass)
1287         record->rclass = rclass;
1288     if (!record->ttl || !ttl)
1289         record->ttl = ttl;
1290     record->rclass &= (uint16_t)(MDNS_CLASS_IN | MDNS_CACHE_FLUSH);
1291     // Never flush PTR record
1292     if (record->type == MDNS_RECORDTYPE_PTR)
1293         record->rclass &= ~(uint16_t)MDNS_CACHE_FLUSH;
1294 }
1295 
1296 static inline void*
1297 mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, const mdns_record_t* records,
1298                            size_t record_count, uint16_t rclass, uint32_t ttl,
1299                            mdns_string_table_t* string_table) {
1300     // Pointer to length of record to be filled at end
1301     void* record_length = 0;
1302     void* record_data = 0;
1303 
1304     size_t remain = 0;
1305     for (size_t irec = 0; data && (irec < record_count); ++irec) {
1306         if (records[irec].type != MDNS_RECORDTYPE_TXT)
1307             continue;
1308 
1309         mdns_record_t record = records[irec];
1310         mdns_record_update_rclass_ttl(&record, rclass, ttl);
1311         if (!record_data) {
1312             data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table);
1313             if (!data)
1314                 return data;
1315             record_length = MDNS_POINTER_OFFSET(data, -2);
1316             record_data = data;
1317         }
1318 
1319         // TXT strings are unlikely to be shared, just make then raw. Also need one byte for
1320         // termination, thus the <= check
1321         size_t string_length = record.data.txt.key.length + record.data.txt.value.length + 1;
1322         if (!data)
1323             return 0;
1324         remain = capacity - MDNS_POINTER_DIFF(data, buffer);
1325         if ((remain <= string_length) || (string_length > 0x3FFF))
1326             return 0;
1327 
1328         unsigned char* strdata = (unsigned char*)data;
1329         *strdata++ = (unsigned char)string_length;
1330         memcpy(strdata, record.data.txt.key.str, record.data.txt.key.length);
1331         strdata += record.data.txt.key.length;
1332         *strdata++ = '=';
1333         memcpy(strdata, record.data.txt.value.str, record.data.txt.value.length);
1334         strdata += record.data.txt.value.length;
1335 
1336         data = strdata;
1337     }
1338 
1339     // Fill record length
1340     if (record_data)
1341         mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data));
1342 
1343     return data;
1344 }
1345 
1346 static inline uint16_t
1347 mdns_answer_get_record_count(const mdns_record_t* records, size_t record_count) {
1348     // TXT records will be coalesced into one record
1349     uint16_t total_count = 0;
1350     uint16_t txt_record = 0;
1351     for (size_t irec = 0; irec < record_count; ++irec) {
1352         if (records[irec].type == MDNS_RECORDTYPE_TXT)
1353             txt_record = 1;
1354         else
1355             ++total_count;
1356     }
1357     return total_count + txt_record;
1358 }
1359 
1360 static inline int
1361 mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer,
1362                           size_t capacity, uint16_t query_id, mdns_record_type_t record_type,
1363                           const char* name, size_t name_length, mdns_record_t answer,
1364                           const mdns_record_t* authority, size_t authority_count,
1365                           const mdns_record_t* additional, size_t additional_count) {
1366     if (capacity < (sizeof(struct mdns_header_t) + 32 + 4))
1367         return -1;
1368 
1369     // According to RFC 6762:
1370     // The cache-flush bit MUST NOT be set in any resource records in a response message
1371     // sent in legacy unicast responses to UDP ports other than 5353.
1372     uint16_t rclass = MDNS_CLASS_IN;
1373     uint32_t ttl = 10;
1374 
1375     // Basic answer structure
1376     struct mdns_header_t* header = (struct mdns_header_t*)buffer;
1377     header->query_id = htons(query_id);
1378     header->flags = htons(0x8400);
1379     header->questions = htons(1);
1380     header->answer_rrs = htons(1);
1381     header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count));
1382     header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count));
1383 
1384     mdns_string_table_t string_table = {{0}, 0, 0};
1385     void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t));
1386 
1387     // Fill in question
1388     data = mdns_answer_add_question_unicast(buffer, capacity, data, record_type, name, name_length,
1389                                             &string_table);
1390 
1391     // Fill in answer
1392     answer.rclass = rclass;
1393     answer.ttl = ttl;
1394     data = mdns_answer_add_record(buffer, capacity, data, answer, &string_table);
1395 
1396     // Fill in authority records
1397     for (size_t irec = 0; data && (irec < authority_count); ++irec) {
1398         mdns_record_t record = authority[irec];
1399         record.rclass = rclass;
1400         if (!record.ttl)
1401             record.ttl = ttl;
1402         data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
1403     }
1404     data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count,
1405                                       rclass, ttl, &string_table);
1406 
1407     // Fill in additional records
1408     for (size_t irec = 0; data && (irec < additional_count); ++irec) {
1409         mdns_record_t record = additional[irec];
1410         record.rclass = rclass;
1411         if (!record.ttl)
1412             record.ttl = ttl;
1413         data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
1414     }
1415     data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count,
1416                                       rclass, ttl, &string_table);
1417     if (!data)
1418         return -1;
1419 
1420     size_t tosend = MDNS_POINTER_DIFF(data, buffer);
1421     return mdns_unicast_send(sock, address, address_size, buffer, tosend);
1422 }
1423 
1424 static inline int
1425 mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, mdns_record_t answer,
1426                                  const mdns_record_t* authority, size_t authority_count,
1427                                  const mdns_record_t* additional, size_t additional_count,
1428                                  uint16_t rclass, uint32_t ttl) {
1429     if (capacity < (sizeof(struct mdns_header_t) + 32 + 4))
1430         return -1;
1431 
1432     // Basic answer structure
1433     struct mdns_header_t* header = (struct mdns_header_t*)buffer;
1434     header->query_id = 0;
1435     header->flags = htons(0x8400);
1436     header->questions = 0;
1437     header->answer_rrs = htons(1);
1438     header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count));
1439     header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count));
1440 
1441     mdns_string_table_t string_table = {{0}, 0, 0};
1442     void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t));
1443 
1444     // Fill in answer
1445     mdns_record_t record = answer;
1446     mdns_record_update_rclass_ttl(&record, rclass, ttl);
1447     data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
1448 
1449     // Fill in authority records
1450     for (size_t irec = 0; data && (irec < authority_count); ++irec) {
1451         record = authority[irec];
1452         mdns_record_update_rclass_ttl(&record, rclass, ttl);
1453         data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
1454     }
1455     data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count,
1456                                       rclass, ttl, &string_table);
1457 
1458     // Fill in additional records
1459     for (size_t irec = 0; data && (irec < additional_count); ++irec) {
1460         record = additional[irec];
1461         mdns_record_update_rclass_ttl(&record, rclass, ttl);
1462         data = mdns_answer_add_record(buffer, capacity, data, record, &string_table);
1463     }
1464     data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count,
1465                                       rclass, ttl, &string_table);
1466     if (!data)
1467         return -1;
1468 
1469     size_t tosend = MDNS_POINTER_DIFF(data, buffer);
1470     return mdns_multicast_send(sock, buffer, tosend);
1471 }
1472 
1473 static inline int
1474 mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
1475                             const mdns_record_t* authority, size_t authority_count,
1476                             const mdns_record_t* additional, size_t additional_count) {
1477     return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority,
1478                                             authority_count, additional, additional_count,
1479                                             MDNS_CLASS_IN, 60);
1480 }
1481 
1482 static inline int
1483 mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
1484                         const mdns_record_t* authority, size_t authority_count,
1485                         const  mdns_record_t* additional, size_t additional_count) {
1486     return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority,
1487                                             authority_count, additional, additional_count,
1488                                             MDNS_CLASS_IN | MDNS_CACHE_FLUSH, 60);
1489 }
1490 
1491 static inline int
1492 mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer,
1493                        const mdns_record_t* authority, size_t authority_count,
1494                        const mdns_record_t* additional, size_t additional_count) {
1495     // Goodbye should have ttl of 0
1496     return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority,
1497                                             authority_count, additional, additional_count,
1498                                             MDNS_CLASS_IN, 0);
1499 }
1500 
1501 static inline mdns_string_t
1502 mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length,
1503                       char* strbuffer, size_t capacity) {
1504     // PTR record is just a string
1505     if ((size >= offset + length) && (length >= 2))
1506         return mdns_string_extract(buffer, size, &offset, strbuffer, capacity);
1507     mdns_string_t empty = {0, 0};
1508     return empty;
1509 }
1510 
1511 static inline mdns_record_srv_t
1512 mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length,
1513                       char* strbuffer, size_t capacity) {
1514     mdns_record_srv_t srv;
1515     memset(&srv, 0, sizeof(mdns_record_srv_t));
1516     // Read the service priority, weight, port number and the discovery name
1517     // SRV record format (http://www.ietf.org/rfc/rfc2782.txt):
1518     // 2 bytes network-order unsigned priority
1519     // 2 bytes network-order unsigned weight
1520     // 2 bytes network-order unsigned port
1521     // string: discovery (domain) name, minimum 2 bytes when compressed
1522     if ((size >= offset + length) && (length >= 8)) {
1523         const uint16_t* recorddata = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset);
1524         srv.priority = mdns_ntohs(recorddata++);
1525         srv.weight = mdns_ntohs(recorddata++);
1526         srv.port = mdns_ntohs(recorddata++);
1527         offset += 6;
1528         srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity);
1529     }
1530     return srv;
1531 }
1532 
1533 static inline struct sockaddr_in*
1534 mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length,
1535                     struct sockaddr_in* addr) {
1536     memset(addr, 0, sizeof(struct sockaddr_in));
1537     addr->sin_family = AF_INET;
1538 #ifdef __APPLE__
1539     addr->sin_len = sizeof(struct sockaddr_in);
1540 #endif
1541     if ((size >= offset + length) && (length == 4))
1542         memcpy(&addr->sin_addr.s_addr, MDNS_POINTER_OFFSET(buffer, offset), 4);
1543     return addr;
1544 }
1545 
1546 static inline struct sockaddr_in6*
1547 mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length,
1548                        struct sockaddr_in6* addr) {
1549     memset(addr, 0, sizeof(struct sockaddr_in6));
1550     addr->sin6_family = AF_INET6;
1551 #ifdef __APPLE__
1552     addr->sin6_len = sizeof(struct sockaddr_in6);
1553 #endif
1554     if ((size >= offset + length) && (length == 16))
1555         memcpy(&addr->sin6_addr, MDNS_POINTER_OFFSET(buffer, offset), 16);
1556     return addr;
1557 }
1558 
1559 static inline size_t
1560 mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length,
1561                       mdns_record_txt_t* records, size_t capacity) {
1562     size_t parsed = 0;
1563     const char* strdata;
1564     size_t end = offset + length;
1565 
1566     if (size < end)
1567         end = size;
1568 
1569     while ((offset < end) && (parsed < capacity)) {
1570         strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset);
1571         size_t sublength = *(const unsigned char*)strdata;
1572 
1573         if (sublength >= (end - offset))
1574             break;
1575 
1576         ++strdata;
1577         offset += sublength + 1;
1578 
1579         size_t separator = sublength;
1580         for (size_t c = 0; c < sublength; ++c) {
1581             // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E]
1582             if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) {
1583                 separator = 0;
1584                 break;
1585             }
1586             if (strdata[c] == '=') {
1587                 separator = c;
1588                 break;
1589             }
1590         }
1591 
1592         if (!separator)
1593             continue;
1594 
1595         if (separator < sublength) {
1596             records[parsed].key.str = strdata;
1597             records[parsed].key.length = separator;
1598             records[parsed].value.str = strdata + separator + 1;
1599             records[parsed].value.length = sublength - (separator + 1);
1600         } else {
1601             records[parsed].key.str = strdata;
1602             records[parsed].key.length = sublength;
1603             records[parsed].value.str = 0;
1604             records[parsed].value.length = 0;
1605         }
1606 
1607         ++parsed;
1608     }
1609 
1610     return parsed;
1611 }
1612 
1613 #ifdef _WIN32
1614 #undef strncasecmp
1615 #endif
1616 
1617 #ifdef __cplusplus
1618 }
1619 #endif