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, ¶m); 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, ¶m); 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