The package rpms/hyperhdr.git has added or updated architecture specific content in its spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s): https://src.fedoraproject.org/cgit/rpms/hyperhdr.git/commit/?id=ce0173c1f4a8....
Change: +ExcludeArch: %{ix86}
Thanks.
Full change: ============
commit 2810b275f0163a90bc0b0eabccdbc1a3f80defdc Author: Vasiliy Glazov vascom2@gmail.com Date: Thu Mar 2 16:22:22 2023 +0300
Unbundle mdns.
diff --git a/hyperhdr.spec b/hyperhdr.spec index d8c4e66..a2cd766 100644 --- a/hyperhdr.spec +++ b/hyperhdr.spec @@ -3,10 +3,9 @@ Version: 19.0.0.0 Release: 2%{?dist} Summary: Ambient lighting
-License: MIT AND LicenseRef-Fedora-Public-Domain AND Apache-2.0 AND BSL-1.0 AND BSD-3-Clause +License: MIT AND Apache-2.0 AND BSL-1.0 AND BSD-3-Clause URL: https://github.com/awawa-dev/HyperHDR Source0: %{url}/archive/refs/tags/v%{version}/%{name}-%{version}.tar.gz -Source1: https://raw.githubusercontent.com/mjansson/mdns/1.4.2/mdns.h Patch0: fix.patch
# https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval @@ -26,12 +25,11 @@ BuildRequires: cmake(flatbuffers) BuildRequires: flatbuffers-compiler BuildRequires: systemd-rpm-macros BuildRequires: desktop-file-utils +BuildRequires: cmake(mdns)
Requires: hicolor-icon-theme Requires: %{name}-common
-Provides: bundled(mdns) = 1.4.2 - %description Open source ambient lighting implementation for television sets based on the video and audio streams analysis, using performance improvements especially @@ -50,11 +48,12 @@ The %{name}-common package contains LUT files for %autosetup -p1 -n HyperHDR-%{version}
mkdir dependencies/bonjour -cp %{SOURCE1} dependencies/bonjour/ +ln -svf %{_includedir}/mdns.h ./dependencies/bonjour/mdns.h sed -i -e 's|file(DOWNLOAD "https://raw.githubusercontent.com/mjansson/mdns/$%7BMJANSSON_MDNS_VERSION%7D...' \ -e 's|"${CMAKE_SOURCE_DIR}/dependencies/bonjour/mdns.h"||' \ -e 's|STATUS MJANSSON_MDNS_STATUS_H)||' CMakeLists.txt
+%build %cmake -G Ninja \ -DCMAKE_CXX_STANDARD=17 \ -DUSE_SYSTEM_FLATBUFFERS_LIBS:BOOL=ON \ @@ -63,10 +62,8 @@ sed -i -e 's|file(DOWNLOAD "https://raw.githubusercontent.com/mjansson/mdns/$%7BM -DBUILD_SHARED_LIBS:BOOL=OFF \ -DPLATFORM=linux
-%build %cmake_build
- %install %cmake_install mkdir -p %{buildroot}%{_datadir}/%{name}/lut/ diff --git a/mdns.h b/mdns.h deleted file mode 100644 index 4b3b03d..0000000 --- a/mdns.h +++ /dev/null @@ -1,1614 +0,0 @@ -/* mdns.h - mDNS/DNS-SD library - Public Domain - 2017 Mattias Jansson - * - * This library provides a cross-platform mDNS and DNS-SD library in C. - * The implementation is based on RFC 6762 and RFC 6763. - * - * The latest source code is always available at - * - * https://github.com/mjansson/mdns - * - * This library is put in the public domain; you can redistribute it and/or modify it without any - * restrictions. - * - */ - -#pragma once - -#include <stdint.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#include <fcntl.h> -#ifdef _WIN32 -#include <Winsock2.h> -#include <Ws2tcpip.h> -#define strncasecmp _strnicmp -#else -#include <unistd.h> -#include <sys/socket.h> -#include <netinet/in.h> -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#define MDNS_INVALID_POS ((size_t)-1) - -#define MDNS_STRING_CONST(s) (s), (sizeof((s)) - 1) -#define MDNS_STRING_ARGS(s) s.str, s.length -#define MDNS_STRING_FORMAT(s) (int)((s).length), s.str - -#define MDNS_POINTER_OFFSET(p, ofs) ((void*)((char*)(p) + (ptrdiff_t)(ofs))) -#define MDNS_POINTER_OFFSET_CONST(p, ofs) ((const void*)((const char*)(p) + (ptrdiff_t)(ofs))) -#define MDNS_POINTER_DIFF(a, b) ((size_t)((const char*)(a) - (const char*)(b))) - -#define MDNS_PORT 5353 -#define MDNS_UNICAST_RESPONSE 0x8000U -#define MDNS_CACHE_FLUSH 0x8000U -#define MDNS_MAX_SUBSTRINGS 64 - -enum mdns_record_type { - MDNS_RECORDTYPE_IGNORE = 0, - // Address - MDNS_RECORDTYPE_A = 1, - // Domain Name pointer - MDNS_RECORDTYPE_PTR = 12, - // Arbitrary text string - MDNS_RECORDTYPE_TXT = 16, - // IP6 Address [Thomson] - MDNS_RECORDTYPE_AAAA = 28, - // Server Selection [RFC2782] - MDNS_RECORDTYPE_SRV = 33, - // Any available records - MDNS_RECORDTYPE_ANY = 255 -}; - -enum mdns_entry_type { - MDNS_ENTRYTYPE_QUESTION = 0, - MDNS_ENTRYTYPE_ANSWER = 1, - MDNS_ENTRYTYPE_AUTHORITY = 2, - MDNS_ENTRYTYPE_ADDITIONAL = 3 -}; - -enum mdns_class { MDNS_CLASS_IN = 1, MDNS_CLASS_ANY = 255 }; - -typedef enum mdns_record_type mdns_record_type_t; -typedef enum mdns_entry_type mdns_entry_type_t; -typedef enum mdns_class mdns_class_t; - -typedef int (*mdns_record_callback_fn)(int sock, const struct sockaddr* from, size_t addrlen, - mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype, - uint16_t rclass, uint32_t ttl, const void* data, size_t size, - size_t name_offset, size_t name_length, size_t record_offset, - size_t record_length, void* user_data); - -typedef struct mdns_string_t mdns_string_t; -typedef struct mdns_string_pair_t mdns_string_pair_t; -typedef struct mdns_string_table_item_t mdns_string_table_item_t; -typedef struct mdns_string_table_t mdns_string_table_t; -typedef struct mdns_record_t mdns_record_t; -typedef struct mdns_record_srv_t mdns_record_srv_t; -typedef struct mdns_record_ptr_t mdns_record_ptr_t; -typedef struct mdns_record_a_t mdns_record_a_t; -typedef struct mdns_record_aaaa_t mdns_record_aaaa_t; -typedef struct mdns_record_txt_t mdns_record_txt_t; -typedef struct mdns_query_t mdns_query_t; - -#ifdef _WIN32 -typedef int mdns_size_t; -typedef int mdns_ssize_t; -#else -typedef size_t mdns_size_t; -typedef ssize_t mdns_ssize_t; -#endif - -struct mdns_string_t { - const char* str; - size_t length; -}; - -struct mdns_string_pair_t { - size_t offset; - size_t length; - int ref; -}; - -struct mdns_string_table_t { - size_t offset[16]; - size_t count; - size_t next; -}; - -struct mdns_record_srv_t { - uint16_t priority; - uint16_t weight; - uint16_t port; - mdns_string_t name; -}; - -struct mdns_record_ptr_t { - mdns_string_t name; -}; - -struct mdns_record_a_t { - struct sockaddr_in addr; -}; - -struct mdns_record_aaaa_t { - struct sockaddr_in6 addr; -}; - -struct mdns_record_txt_t { - mdns_string_t key; - mdns_string_t value; -}; - -struct mdns_record_t { - mdns_string_t name; - mdns_record_type_t type; - union mdns_record_data { - mdns_record_ptr_t ptr; - mdns_record_srv_t srv; - mdns_record_a_t a; - mdns_record_aaaa_t aaaa; - mdns_record_txt_t txt; - } data; - uint16_t rclass; - uint32_t ttl; -}; - -struct mdns_header_t { - uint16_t query_id; - uint16_t flags; - uint16_t questions; - uint16_t answer_rrs; - uint16_t authority_rrs; - uint16_t additional_rrs; -}; - -struct mdns_query_t { - mdns_record_type_t type; - const char* name; - size_t length; -}; - -// mDNS/DNS-SD public API - -//! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass -//! in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. To -//! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a -//! random user level ephemeral port. To run discovery service listening for incoming discoveries -//! and queries, you must set MDNS_PORT as port. -static inline int -mdns_socket_open_ipv4(const struct sockaddr_in* saddr); - -//! Setup an already opened IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, -//! pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. -//! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a -//! random user level ephemeral port. To run discovery service listening for incoming discoveries -//! and queries, you must set MDNS_PORT as port. -static inline int -mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr); - -//! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass -//! in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. To -//! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a -//! random user level ephemeral port. To run discovery service listening for incoming discoveries -//! and queries, you must set MDNS_PORT as port. -static inline int -mdns_socket_open_ipv6(const struct sockaddr_in6* saddr); - -//! Setup an already opened IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, -//! pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. -//! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a -//! random user level ephemeral port. To run discovery service listening for incoming discoveries -//! and queries, you must set MDNS_PORT as port. -static inline int -mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr); - -//! Close a socket opened with mdns_socket_open_ipv4 and mdns_socket_open_ipv6. -static inline void -mdns_socket_close(int sock); - -//! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been opened -//! on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be 32 bit -//! aligned. Parsing is stopped when callback function returns non-zero. Returns the number of -//! queries parsed. -static inline size_t -mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, - void* user_data); - -//! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns 0 -//! on success, or <0 if error. -static inline int -mdns_discovery_send(int sock); - -//! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to -//! the given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback -//! function returns non-zero. Returns the number of responses parsed. -static inline size_t -mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, - void* user_data); - -//! Send a multicast mDNS query on the given socket for the given service name. The supplied buffer -//! will be used to build the query packet and must be 32 bit aligned. The query ID can be set to -//! non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for -//! multicast queries. The query will request a unicast response if the socket is bound to an -//! ephemeral port, or a multicast response if the socket is bound to mDNS port 5353. Returns the -//! used query ID, or <0 if error. -static inline int -mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, - size_t capacity, uint16_t query_id); - -//! Send a multicast mDNS query on the given socket for the given service names. The supplied buffer -//! will be used to build the query packet and must be 32 bit aligned. The query ID can be set to -//! non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for -//! multicast queries. Each additional service name query consists of a triplet - a record type -//! (mdns_record_type_t), a name string pointer (const char*) and a name length (size_t). The list -//! of variable arguments should be terminated with a record type of 0. The query will request a -//! unicast response if the socket is bound to an ephemeral port, or a multicast response if the -//! socket is bound to mDNS port 5353. Returns the used query ID, or <0 if error. -static inline int -mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, - size_t capacity, uint16_t query_id); - -//! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering -//! out any responses not matching the given query ID. Set the query ID to 0 to parse all responses, -//! even if it is not matching the query ID set in a specific query. Any data will be piped to the -//! given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback -//! function returns non-zero. Returns the number of responses parsed. -static inline size_t -mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, - void* user_data, int query_id); - -//! Send a variable unicast mDNS query answer to any question with variable number of records to the -//! given address. Use the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query -//! recieved to determine if the answer should be sent unicast (bit set) or multicast (bit not set). -//! Buffer must be 32 bit aligned. The record type and name should match the data from the query -//! recieved. Returns 0 if success, or <0 if error. -static inline int -mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, - size_t capacity, uint16_t query_id, mdns_record_type_t record_type, - const char* name, size_t name_length, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count); - -//! Send a variable multicast mDNS query answer to any question with variable number of records. Use -//! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine -//! if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be 32 bit -//! aligned. Returns 0 if success, or <0 if error. -static inline int -mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count); - -//! Send a variable multicast mDNS announcement (as an unsolicited answer) with variable number of -//! records.Buffer must be 32 bit aligned. Returns 0 if success, or <0 if error. Use this on service -//! startup to announce your instance to the local network. -static inline int -mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count); - -//! Send a variable multicast mDNS announcement. Use this on service end for removing the resource -//! from the local network. The records must be identical to the according announcement. -static inline int -mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count); - -// Parse records functions - -//! Parse a PTR record, returns the name in the record -static inline mdns_string_t -mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, - char* strbuffer, size_t capacity); - -//! Parse a SRV record, returns the priority, weight, port and name in the record -static inline mdns_record_srv_t -mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, - char* strbuffer, size_t capacity); - -//! Parse an A record, returns the IPv4 address in the record -static inline struct sockaddr_in* -mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, - struct sockaddr_in* addr); - -//! Parse an AAAA record, returns the IPv6 address in the record -static inline struct sockaddr_in6* -mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, - struct sockaddr_in6* addr); - -//! Parse a TXT record, returns the number of key=value records parsed and stores the key-value -//! pairs in the supplied buffer -static inline size_t -mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, - mdns_record_txt_t* records, size_t capacity); - -// Internal functions - -static inline mdns_string_t -mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity); - -static inline int -mdns_string_skip(const void* buffer, size_t size, size_t* offset); - -static inline size_t -mdns_string_find(const char* str, size_t length, char c, size_t offset); - -//! Compare if two strings are equal. If the strings are equal it returns >0 and the offset variables are -//! updated to the end of the corresponding strings. If the strings are not equal it returns 0 and -//! the offset variables are NOT updated. -static inline int -mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs, - size_t size_rhs, size_t* ofs_rhs); - -static inline void* -mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, - mdns_string_table_t* string_table); - -static inline size_t -mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, - const char* str, size_t first_length, size_t total_length); - -// Implementations - -static inline uint16_t -mdns_ntohs(const void* data) { - uint16_t aligned; - memcpy(&aligned, data, sizeof(uint16_t)); - return ntohs(aligned); -} - -static inline uint32_t -mdns_ntohl(const void* data) { - uint32_t aligned; - memcpy(&aligned, data, sizeof(uint32_t)); - return ntohl(aligned); -} - -static inline void* -mdns_htons(void* data, uint16_t val) { - val = htons(val); - memcpy(data, &val, sizeof(uint16_t)); - return MDNS_POINTER_OFFSET(data, sizeof(uint16_t)); -} - -static inline void* -mdns_htonl(void* data, uint32_t val) { - val = htonl(val); - memcpy(data, &val, sizeof(uint32_t)); - return MDNS_POINTER_OFFSET(data, sizeof(uint32_t)); -} - -static inline int -mdns_socket_open_ipv4(const struct sockaddr_in* saddr) { - int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) - return -1; - if (mdns_socket_setup_ipv4(sock, saddr)) { - mdns_socket_close(sock); - return -1; - } - return sock; -} - -static inline int -mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) { - unsigned char ttl = 1; - unsigned char loopback = 1; - unsigned int reuseaddr = 1; - struct ip_mreq req; - - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); -#ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); -#endif - setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)); - setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); - - memset(&req, 0, sizeof(req)); - req.imr_multiaddr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); - if (saddr) - req.imr_interface = saddr->sin_addr; - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof(req))) - return -1; - - struct sockaddr_in sock_addr; - if (!saddr) { - memset(&sock_addr, 0, sizeof(struct sockaddr_in)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = INADDR_ANY; -#ifdef __APPLE__ - sock_addr.sin_len = sizeof(struct sockaddr_in); -#endif - } else { - memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in)); - setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&sock_addr.sin_addr, - sizeof(sock_addr.sin_addr)); -#ifndef _WIN32 - sock_addr.sin_addr.s_addr = INADDR_ANY; -#endif - } - - if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in))) - return -1; - -#ifdef _WIN32 - unsigned long param = 1; - ioctlsocket(sock, FIONBIO, ¶m); -#else - const int flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); -#endif - - return 0; -} - -static inline int -mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) { - int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) - return -1; - if (mdns_socket_setup_ipv6(sock, saddr)) { - mdns_socket_close(sock); - return -1; - } - return sock; -} - -static inline int -mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) { - int hops = 1; - unsigned int loopback = 1; - unsigned int reuseaddr = 1; - struct ipv6_mreq req; - - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); -#ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); -#endif - setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops)); - setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); - - memset(&req, 0, sizeof(req)); - req.ipv6mr_multiaddr.s6_addr[0] = 0xFF; - req.ipv6mr_multiaddr.s6_addr[1] = 0x02; - req.ipv6mr_multiaddr.s6_addr[15] = 0xFB; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req))) - return -1; - - struct sockaddr_in6 sock_addr; - if (!saddr) { - memset(&sock_addr, 0, sizeof(struct sockaddr_in6)); - sock_addr.sin6_family = AF_INET6; - sock_addr.sin6_addr = in6addr_any; -#ifdef __APPLE__ - sock_addr.sin6_len = sizeof(struct sockaddr_in6); -#endif - } else { - memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in6)); - unsigned int ifindex = 0; - setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&ifindex, sizeof(ifindex)); -#ifndef _WIN32 - sock_addr.sin6_addr = in6addr_any; -#endif - } - - if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in6))) - return -1; - -#ifdef _WIN32 - unsigned long param = 1; - ioctlsocket(sock, FIONBIO, ¶m); -#else - const int flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); -#endif - - return 0; -} - -static inline void -mdns_socket_close(int sock) { -#ifdef _WIN32 - closesocket(sock); -#else - close(sock); -#endif -} - -static inline int -mdns_is_string_ref(uint8_t val) { - return (0xC0 == (val & 0xC0)); -} - -static inline mdns_string_pair_t -mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { - const uint8_t* buffer = (const uint8_t*)rawdata; - mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0}; - if (offset >= size) - return pair; - if (!buffer[offset]) { - pair.offset = offset; - return pair; - } - int recursion = 0; - while (mdns_is_string_ref(buffer[offset])) { - if (size < offset + 2) - return pair; - - offset = mdns_ntohs(MDNS_POINTER_OFFSET(buffer, offset)) & 0x3fff; - if (offset >= size) - return pair; - - pair.ref = 1; - if (++recursion > 16) - return pair; - } - - size_t length = (size_t)buffer[offset++]; - if (size < offset + length) - return pair; - - pair.offset = offset; - pair.length = length; - - return pair; -} - -static inline int -mdns_string_skip(const void* buffer, size_t size, size_t* offset) { - size_t cur = *offset; - mdns_string_pair_t substr; - unsigned int counter = 0; - do { - substr = mdns_get_next_substring(buffer, size, cur); - if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS)) - return 0; - if (substr.ref) { - *offset = cur + 2; - return 1; - } - cur = substr.offset + substr.length; - } while (substr.length); - - *offset = cur + 1; - return 1; -} - -static inline int -mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs, - size_t size_rhs, size_t* ofs_rhs) { - size_t lhs_cur = *ofs_lhs; - size_t rhs_cur = *ofs_rhs; - size_t lhs_end = MDNS_INVALID_POS; - size_t rhs_end = MDNS_INVALID_POS; - mdns_string_pair_t lhs_substr; - mdns_string_pair_t rhs_substr; - unsigned int counter = 0; - do { - lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur); - rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur); - if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS) || - (counter++ > MDNS_MAX_SUBSTRINGS)) - return 0; - if (lhs_substr.length != rhs_substr.length) - return 0; - if (strncasecmp((const char*)MDNS_POINTER_OFFSET_CONST(buffer_rhs, rhs_substr.offset), - (const char*)MDNS_POINTER_OFFSET_CONST(buffer_lhs, lhs_substr.offset), - rhs_substr.length)) - return 0; - if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS)) - lhs_end = lhs_cur + 2; - if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS)) - rhs_end = rhs_cur + 2; - lhs_cur = lhs_substr.offset + lhs_substr.length; - rhs_cur = rhs_substr.offset + rhs_substr.length; - } while (lhs_substr.length); - - if (lhs_end == MDNS_INVALID_POS) - lhs_end = lhs_cur + 1; - *ofs_lhs = lhs_end; - - if (rhs_end == MDNS_INVALID_POS) - rhs_end = rhs_cur + 1; - *ofs_rhs = rhs_end; - - return 1; -} - -static inline mdns_string_t -mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity) { - size_t cur = *offset; - size_t end = MDNS_INVALID_POS; - mdns_string_pair_t substr; - mdns_string_t result; - result.str = str; - result.length = 0; - char* dst = str; - unsigned int counter = 0; - size_t remain = capacity; - do { - substr = mdns_get_next_substring(buffer, size, cur); - if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS)) - return result; - if (substr.ref && (end == MDNS_INVALID_POS)) - end = cur + 2; - if (substr.length) { - size_t to_copy = (substr.length < remain) ? substr.length : remain; - memcpy(dst, (const char*)buffer + substr.offset, to_copy); - dst += to_copy; - remain -= to_copy; - if (remain) { - *dst++ = '.'; - --remain; - } - } - cur = substr.offset + substr.length; - } while (substr.length); - - if (end == MDNS_INVALID_POS) - end = cur + 1; - *offset = end; - - result.length = capacity - remain; - return result; -} - -static inline size_t -mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, - const char* str, size_t first_length, size_t total_length) { - if (!string_table) - return MDNS_INVALID_POS; - - for (size_t istr = 0; istr < string_table->count; ++istr) { - if (string_table->offset[istr] >= capacity) - continue; - size_t offset = 0; - mdns_string_pair_t sub_string = - mdns_get_next_substring(buffer, capacity, string_table->offset[istr]); - if (!sub_string.length || (sub_string.length != first_length)) - continue; - if (memcmp(str, MDNS_POINTER_OFFSET(buffer, sub_string.offset), sub_string.length)) - continue; - - // Initial substring matches, now match all remaining substrings - offset += first_length + 1; - while (offset < total_length) { - size_t dot_pos = mdns_string_find(str, total_length, '.', offset); - if (dot_pos == MDNS_INVALID_POS) - dot_pos = total_length; - size_t current_length = dot_pos - offset; - - sub_string = - mdns_get_next_substring(buffer, capacity, sub_string.offset + sub_string.length); - if (!sub_string.length || (sub_string.length != current_length)) - break; - if (memcmp(str + offset, MDNS_POINTER_OFFSET(buffer, sub_string.offset), - sub_string.length)) - break; - - offset = dot_pos + 1; - } - - // Return reference offset if entire string matches - if (offset >= total_length) - return string_table->offset[istr]; - } - - return MDNS_INVALID_POS; -} - -static inline void -mdns_string_table_add(mdns_string_table_t* string_table, size_t offset) { - if (!string_table) - return; - - string_table->offset[string_table->next] = offset; - - size_t table_capacity = sizeof(string_table->offset) / sizeof(string_table->offset[0]); - if (++string_table->count > table_capacity) - string_table->count = table_capacity; - if (++string_table->next >= table_capacity) - string_table->next = 0; -} - -static inline size_t -mdns_string_find(const char* str, size_t length, char c, size_t offset) { - const void* found; - if (offset >= length) - return MDNS_INVALID_POS; - found = memchr(str + offset, c, length - offset); - if (found) - return (size_t)MDNS_POINTER_DIFF(found, str); - return MDNS_INVALID_POS; -} - -static inline void* -mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { - if (capacity < 2) - return 0; - return mdns_htons(data, 0xC000 | (uint16_t)ref_offset); -} - -static inline void* -mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, - mdns_string_table_t* string_table) { - size_t last_pos = 0; - size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (name[length - 1] == '.') - --length; - while (last_pos < length) { - size_t pos = mdns_string_find(name, length, '.', last_pos); - size_t sub_length = ((pos != MDNS_INVALID_POS) ? pos : length) - last_pos; - size_t total_length = length - last_pos; - - size_t ref_offset = - mdns_string_table_find(string_table, buffer, capacity, - (char*)MDNS_POINTER_OFFSET(name, last_pos), sub_length, - total_length); - if (ref_offset != MDNS_INVALID_POS) - return mdns_string_make_ref(data, remain, ref_offset); - - if (remain <= (sub_length + 1)) - return 0; - - *(unsigned char*)data = (unsigned char)sub_length; - memcpy(MDNS_POINTER_OFFSET(data, 1), name + last_pos, sub_length); - mdns_string_table_add(string_table, MDNS_POINTER_DIFF(data, buffer)); - - data = MDNS_POINTER_OFFSET(data, sub_length + 1); - last_pos = ((pos != MDNS_INVALID_POS) ? pos + 1 : length); - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - } - - if (!remain) - return 0; - - *(unsigned char*)data = 0; - return MDNS_POINTER_OFFSET(data, 1); -} - -static inline size_t -mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const void* buffer, - size_t size, size_t* offset, mdns_entry_type_t type, uint16_t query_id, - size_t records, mdns_record_callback_fn callback, void* user_data) { - size_t parsed = 0; - for (size_t i = 0; i < records; ++i) { - size_t name_offset = *offset; - mdns_string_skip(buffer, size, offset); - if (((*offset) + 10) > size) - return parsed; - size_t name_length = (*offset) - name_offset; - const uint16_t* data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, *offset); - - uint16_t rtype = mdns_ntohs(data++); - uint16_t rclass = mdns_ntohs(data++); - uint32_t ttl = mdns_ntohl(data); - data += 2; - uint16_t length = mdns_ntohs(data++); - - *offset += 10; - - if (length <= (size - (*offset))) { - ++parsed; - if (callback && - callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size, - name_offset, name_length, *offset, length, user_data)) - break; - } - - *offset += length; - } - return parsed; -} - -static inline int -mdns_unicast_send(int sock, const void* address, size_t address_size, const void* buffer, - size_t size) { - if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address, - (socklen_t)address_size) < 0) - return -1; - return 0; -} - -static inline int -mdns_multicast_send(int sock, const void* buffer, size_t size) { - struct sockaddr_storage addr_storage; - struct sockaddr_in addr; - struct sockaddr_in6 addr6; - struct sockaddr* saddr = (struct sockaddr*)&addr_storage; - socklen_t saddrlen = sizeof(struct sockaddr_storage); - if (getsockname(sock, saddr, &saddrlen)) - return -1; - if (saddr->sa_family == AF_INET6) { - memset(&addr6, 0, sizeof(addr6)); - addr6.sin6_family = AF_INET6; -#ifdef __APPLE__ - addr6.sin6_len = sizeof(addr6); -#endif - addr6.sin6_addr.s6_addr[0] = 0xFF; - addr6.sin6_addr.s6_addr[1] = 0x02; - addr6.sin6_addr.s6_addr[15] = 0xFB; - addr6.sin6_port = htons((unsigned short)MDNS_PORT); - saddr = (struct sockaddr*)&addr6; - saddrlen = sizeof(addr6); - } else { - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; -#ifdef __APPLE__ - addr.sin_len = sizeof(addr); -#endif - addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); - addr.sin_port = htons((unsigned short)MDNS_PORT); - saddr = (struct sockaddr*)&addr; - saddrlen = sizeof(addr); - } - - if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0) - return -1; - return 0; -} - -static const uint8_t mdns_services_query[] = { - // Query ID - 0x00, 0x00, - // Flags - 0x00, 0x00, - // 1 question - 0x00, 0x01, - // No answer, authority or additional RRs - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // _services._dns-sd._udp.local. - 0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', 0x07, '_', 'd', 'n', 's', '-', 's', 'd', - 0x04, '_', 'u', 'd', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, - // PTR record - 0x00, MDNS_RECORDTYPE_PTR, - // QU (unicast response) and class IN - 0x80, MDNS_CLASS_IN}; - -static inline int -mdns_discovery_send(int sock) { - return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query)); -} - -static inline size_t -mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, - void* user_data) { - struct sockaddr_in6 addr; - struct sockaddr* saddr = (struct sockaddr*)&addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); -#ifdef __APPLE__ - saddr->sa_len = sizeof(addr); -#endif - mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); - if (ret <= 0) - return 0; - - size_t data_size = (size_t)ret; - size_t records = 0; - const uint16_t* data = (uint16_t*)buffer; - - uint16_t query_id = mdns_ntohs(data++); - uint16_t flags = mdns_ntohs(data++); - uint16_t questions = mdns_ntohs(data++); - uint16_t answer_rrs = mdns_ntohs(data++); - uint16_t authority_rrs = mdns_ntohs(data++); - uint16_t additional_rrs = mdns_ntohs(data++); - - // According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case) - if (query_id || (flags != 0x8400)) - return 0; // Not a reply to our question - - // It seems some implementations do not fill the correct questions field, - // so ignore this check for now and only validate answer string - // if (questions != 1) - // return 0; - - int i; - for (i = 0; i < questions; ++i) { - size_t offset = MDNS_POINTER_DIFF(data, buffer); - size_t verify_offset = 12; - // Verify it's our question, _services._dns-sd._udp.local. - if (!mdns_string_equal(buffer, data_size, &offset, mdns_services_query, - sizeof(mdns_services_query), &verify_offset)) - return 0; - data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset); - - uint16_t rtype = mdns_ntohs(data++); - uint16_t rclass = mdns_ntohs(data++); - - // Make sure we get a reply based on our PTR question for class IN - if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN)) - return 0; - } - - for (i = 0; i < answer_rrs; ++i) { - size_t offset = MDNS_POINTER_DIFF(data, buffer); - size_t verify_offset = 12; - // Verify it's an answer to our question, _services._dns-sd._udp.local. - size_t name_offset = offset; - int is_answer = mdns_string_equal(buffer, data_size, &offset, mdns_services_query, - sizeof(mdns_services_query), &verify_offset); - if (!is_answer && !mdns_string_skip(buffer, data_size, &offset)) - break; - size_t name_length = offset - name_offset; - if ((offset + 10) > data_size) - return records; - data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset); - - uint16_t rtype = mdns_ntohs(data++); - uint16_t rclass = mdns_ntohs(data++); - uint32_t ttl = mdns_ntohl(data); - data += 2; - uint16_t length = mdns_ntohs(data++); - if (length > (data_size - offset)) - return 0; - - if (is_answer) { - ++records; - offset = MDNS_POINTER_DIFF(data, buffer); - if (callback && - callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, - buffer, data_size, name_offset, name_length, offset, length, user_data)) - return records; - } - data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length); - } - - size_t total_records = records; - size_t offset = MDNS_POINTER_DIFF(data, buffer); - records = - mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); - total_records += records; - if (records != authority_rrs) - return total_records; - - records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, - user_data); - total_records += records; - if (records != additional_rrs) - return total_records; - - return total_records; -} - -static inline size_t -mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, - void* user_data) { - struct sockaddr_in6 addr; - struct sockaddr* saddr = (struct sockaddr*)&addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); -#ifdef __APPLE__ - saddr->sa_len = sizeof(addr); -#endif - mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); - if (ret <= 0) - return 0; - - size_t data_size = (size_t)ret; - const uint16_t* data = (const uint16_t*)buffer; - - uint16_t query_id = mdns_ntohs(data++); - uint16_t flags = mdns_ntohs(data++); - uint16_t questions = mdns_ntohs(data++); - uint16_t answer_rrs = mdns_ntohs(data++); - uint16_t authority_rrs = mdns_ntohs(data++); - uint16_t additional_rrs = mdns_ntohs(data++); - - size_t records; - size_t total_records = 0; - for (int iquestion = 0; iquestion < questions; ++iquestion) { - size_t question_offset = MDNS_POINTER_DIFF(data, buffer); - size_t offset = question_offset; - size_t verify_offset = 12; - int dns_sd = 0; - if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, - sizeof(mdns_services_query), &verify_offset)) { - dns_sd = 1; - } else if (!mdns_string_skip(buffer, data_size, &offset)) { - break; - } - size_t length = offset - question_offset; - data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); - - uint16_t rtype = mdns_ntohs(data++); - uint16_t rclass = mdns_ntohs(data++); - uint16_t class_without_flushbit = rclass & ~MDNS_CACHE_FLUSH; - - // Make sure we get a question of class IN or ANY - if (!((class_without_flushbit == MDNS_CLASS_IN) || - (class_without_flushbit == MDNS_CLASS_ANY))) { - break; - } - - if (dns_sd && flags) - continue; - - ++total_records; - if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, - rclass, 0, buffer, data_size, question_offset, length, - question_offset, length, user_data)) - return total_records; - } - - size_t offset = MDNS_POINTER_DIFF(data, buffer); - records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data); - total_records += records; - if (records != answer_rrs) - return total_records; - - records = - mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); - total_records += records; - if (records != authority_rrs) - return total_records; - - records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, - user_data); - - return total_records; -} - -static inline int -mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, - size_t capacity, uint16_t query_id) { - mdns_query_t query; - query.type = type; - query.name = name; - query.length = length; - return mdns_multiquery_send(sock, &query, 1, buffer, capacity, query_id); -} - -static inline int -mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, size_t capacity, - uint16_t query_id) { - if (!count || (capacity < (sizeof(struct mdns_header_t) + (6 * count)))) - return -1; - - // Ask for a unicast response since it's a one-shot query - uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE; - - struct sockaddr_storage addr_storage; - struct sockaddr* saddr = (struct sockaddr*)&addr_storage; - socklen_t saddrlen = sizeof(addr_storage); - if (getsockname(sock, saddr, &saddrlen) == 0) { - if ((saddr->sa_family == AF_INET) && - (ntohs(((struct sockaddr_in*)saddr)->sin_port) == MDNS_PORT)) - rclass &= ~MDNS_UNICAST_RESPONSE; - else if ((saddr->sa_family == AF_INET6) && - (ntohs(((struct sockaddr_in6*)saddr)->sin6_port) == MDNS_PORT)) - rclass &= ~MDNS_UNICAST_RESPONSE; - } - - struct mdns_header_t* header = (struct mdns_header_t*)buffer; - // Query ID - header->query_id = htons((unsigned short)query_id); - // Flags - header->flags = 0; - // Questions - header->questions = htons((unsigned short)count); - // No answer, authority or additional RRs - header->answer_rrs = 0; - header->authority_rrs = 0; - header->additional_rrs = 0; - // Fill in questions - void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - for (size_t iq = 0; iq < count; ++iq) { - // Name string - data = mdns_string_make(buffer, capacity, data, query[iq].name, query[iq].length, 0); - if (!data) - return -1; - // Record type - data = mdns_htons(data, query[iq].type); - //! Optional unicast response based on local port, class IN - data = mdns_htons(data, rclass); - } - - size_t tosend = MDNS_POINTER_DIFF(data, buffer); - if (mdns_multicast_send(sock, buffer, (size_t)tosend)) - return -1; - return query_id; -} - -static inline size_t -mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, - void* user_data, int only_query_id) { - struct sockaddr_in6 addr; - struct sockaddr* saddr = (struct sockaddr*)&addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); -#ifdef __APPLE__ - saddr->sa_len = sizeof(addr); -#endif - mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); - if (ret <= 0) - return 0; - - size_t data_size = (size_t)ret; - const uint16_t* data = (const uint16_t*)buffer; - - uint16_t query_id = mdns_ntohs(data++); - uint16_t flags = mdns_ntohs(data++); - uint16_t questions = mdns_ntohs(data++); - uint16_t answer_rrs = mdns_ntohs(data++); - uint16_t authority_rrs = mdns_ntohs(data++); - uint16_t additional_rrs = mdns_ntohs(data++); - (void)sizeof(flags); - - if ((only_query_id > 0) && (query_id != only_query_id)) - return 0; // Not a reply to the wanted one-shot query - - if (questions > 1) - return 0; - - // Skip questions part - int i; - for (i = 0; i < questions; ++i) { - size_t offset = MDNS_POINTER_DIFF(data, buffer); - if (!mdns_string_skip(buffer, data_size, &offset)) - return 0; - data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); - // Record type and class not used, skip - // uint16_t rtype = mdns_ntohs(data++); - // uint16_t rclass = mdns_ntohs(data++); - data += 2; - } - - size_t records = 0; - size_t total_records = 0; - size_t offset = MDNS_POINTER_DIFF(data, buffer); - records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data); - total_records += records; - if (records != answer_rrs) - return total_records; - - records = - mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); - total_records += records; - if (records != authority_rrs) - return total_records; - - records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, - MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, - user_data); - total_records += records; - if (records != additional_rrs) - return total_records; - - return total_records; -} - -static inline void* -mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, - mdns_record_type_t record_type, const char* name, - size_t name_length, mdns_string_table_t* string_table) { - data = mdns_string_make(buffer, capacity, data, name, name_length, string_table); - if (!data) - return 0; - size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (remain < 4) - return 0; - - data = mdns_htons(data, record_type); - data = mdns_htons(data, MDNS_UNICAST_RESPONSE | MDNS_CLASS_IN); - - return data; -} - -static inline void* -mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_record_t record, - mdns_string_table_t* string_table) { - data = mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table); - if (!data) - return 0; - size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if (remain < 10) - return 0; - - data = mdns_htons(data, record.type); - data = mdns_htons(data, record.rclass); - data = mdns_htonl(data, record.ttl); - data = mdns_htons(data, 0); // Length, to be filled later - return data; -} - -static inline void* -mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t record, - mdns_string_table_t* string_table) { - // TXT records will be coalesced into one record later - if (!data || (record.type == MDNS_RECORDTYPE_TXT)) - return data; - - data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table); - if (!data) - return 0; - - // Pointer to length of record to be filled at end - void* record_length = MDNS_POINTER_OFFSET(data, -2); - void* record_data = data; - - size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); - switch (record.type) { - case MDNS_RECORDTYPE_PTR: - data = mdns_string_make(buffer, capacity, data, record.data.ptr.name.str, - record.data.ptr.name.length, string_table); - break; - - case MDNS_RECORDTYPE_SRV: - if (remain <= 6) - return 0; - data = mdns_htons(data, record.data.srv.priority); - data = mdns_htons(data, record.data.srv.weight); - data = mdns_htons(data, record.data.srv.port); - data = mdns_string_make(buffer, capacity, data, record.data.srv.name.str, - record.data.srv.name.length, string_table); - break; - - case MDNS_RECORDTYPE_A: - if (remain < 4) - return 0; - memcpy(data, &record.data.a.addr.sin_addr.s_addr, 4); - data = MDNS_POINTER_OFFSET(data, 4); - break; - - case MDNS_RECORDTYPE_AAAA: - if (remain < 16) - return 0; - memcpy(data, &record.data.aaaa.addr.sin6_addr, 16); // ipv6 address - data = MDNS_POINTER_OFFSET(data, 16); - break; - - default: - break; - } - - if (!data) - return 0; - - // Fill record length - mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); - return data; -} - -static inline void -mdns_record_update_rclass_ttl(mdns_record_t* record, uint16_t rclass, uint32_t ttl) { - if (!record->rclass) - record->rclass = rclass; - if (!record->ttl || !ttl) - record->ttl = ttl; - record->rclass &= (uint16_t)(MDNS_CLASS_IN | MDNS_CACHE_FLUSH); - // Never flush PTR record - if (record->type == MDNS_RECORDTYPE_PTR) - record->rclass &= ~(uint16_t)MDNS_CACHE_FLUSH; -} - -static inline void* -mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, const mdns_record_t* records, - size_t record_count, uint16_t rclass, uint32_t ttl, - mdns_string_table_t* string_table) { - // Pointer to length of record to be filled at end - void* record_length = 0; - void* record_data = 0; - - size_t remain = 0; - for (size_t irec = 0; data && (irec < record_count); ++irec) { - if (records[irec].type != MDNS_RECORDTYPE_TXT) - continue; - - mdns_record_t record = records[irec]; - mdns_record_update_rclass_ttl(&record, rclass, ttl); - if (!record_data) { - data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table); - if (!data) - return data; - record_length = MDNS_POINTER_OFFSET(data, -2); - record_data = data; - } - - // TXT strings are unlikely to be shared, just make then raw. Also need one byte for - // termination, thus the <= check - size_t string_length = record.data.txt.key.length + record.data.txt.value.length + 1; - if (!data) - return 0; - remain = capacity - MDNS_POINTER_DIFF(data, buffer); - if ((remain <= string_length) || (string_length > 0x3FFF)) - return 0; - - unsigned char* strdata = (unsigned char*)data; - *strdata++ = (unsigned char)string_length; - memcpy(strdata, record.data.txt.key.str, record.data.txt.key.length); - strdata += record.data.txt.key.length; - *strdata++ = '='; - memcpy(strdata, record.data.txt.value.str, record.data.txt.value.length); - strdata += record.data.txt.value.length; - - data = strdata; - } - - // Fill record length - if (record_data) - mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); - - return data; -} - -static inline uint16_t -mdns_answer_get_record_count(const mdns_record_t* records, size_t record_count) { - // TXT records will be coalesced into one record - uint16_t total_count = 0; - uint16_t txt_record = 0; - for (size_t irec = 0; irec < record_count; ++irec) { - if (records[irec].type == MDNS_RECORDTYPE_TXT) - txt_record = 1; - else - ++total_count; - } - return total_count + txt_record; -} - -static inline int -mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, - size_t capacity, uint16_t query_id, mdns_record_type_t record_type, - const char* name, size_t name_length, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count) { - if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) - return -1; - - // According to RFC 6762: - // The cache-flush bit MUST NOT be set in any resource records in a response message - // sent in legacy unicast responses to UDP ports other than 5353. - uint16_t rclass = MDNS_CLASS_IN; - uint32_t ttl = 10; - - // Basic answer structure - struct mdns_header_t* header = (struct mdns_header_t*)buffer; - header->query_id = htons(query_id); - header->flags = htons(0x8400); - header->questions = htons(1); - header->answer_rrs = htons(1); - header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); - header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); - - mdns_string_table_t string_table = {{0}, 0, 0}; - void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - - // Fill in question - data = mdns_answer_add_question_unicast(buffer, capacity, data, record_type, name, name_length, - &string_table); - - // Fill in answer - answer.rclass = rclass; - answer.ttl = ttl; - data = mdns_answer_add_record(buffer, capacity, data, answer, &string_table); - - // Fill in authority records - for (size_t irec = 0; data && (irec < authority_count); ++irec) { - mdns_record_t record = authority[irec]; - record.rclass = rclass; - if (!record.ttl) - record.ttl = ttl; - data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); - } - data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, - rclass, ttl, &string_table); - - // Fill in additional records - for (size_t irec = 0; data && (irec < additional_count); ++irec) { - mdns_record_t record = additional[irec]; - record.rclass = rclass; - if (!record.ttl) - record.ttl = ttl; - data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); - } - data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, - rclass, ttl, &string_table); - if (!data) - return -1; - - size_t tosend = MDNS_POINTER_DIFF(data, buffer); - return mdns_unicast_send(sock, address, address_size, buffer, tosend); -} - -static inline int -mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count, - uint16_t rclass, uint32_t ttl) { - if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) - return -1; - - // Basic answer structure - struct mdns_header_t* header = (struct mdns_header_t*)buffer; - header->query_id = 0; - header->flags = htons(0x8400); - header->questions = 0; - header->answer_rrs = htons(1); - header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); - header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); - - mdns_string_table_t string_table = {{0}, 0, 0}; - void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); - - // Fill in answer - mdns_record_t record = answer; - mdns_record_update_rclass_ttl(&record, rclass, ttl); - data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); - - // Fill in authority records - for (size_t irec = 0; data && (irec < authority_count); ++irec) { - record = authority[irec]; - mdns_record_update_rclass_ttl(&record, rclass, ttl); - data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); - } - data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, - rclass, ttl, &string_table); - - // Fill in additional records - for (size_t irec = 0; data && (irec < additional_count); ++irec) { - record = additional[irec]; - mdns_record_update_rclass_ttl(&record, rclass, ttl); - data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); - } - data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, - rclass, ttl, &string_table); - if (!data) - return -1; - - size_t tosend = MDNS_POINTER_DIFF(data, buffer); - return mdns_multicast_send(sock, buffer, tosend); -} - -static inline int -mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count) { - return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, - authority_count, additional, additional_count, - MDNS_CLASS_IN, 60); -} - -static inline int -mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count) { - return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, - authority_count, additional, additional_count, - MDNS_CLASS_IN | MDNS_CACHE_FLUSH, 60); -} - -static inline int -mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, - const mdns_record_t* authority, size_t authority_count, - const mdns_record_t* additional, size_t additional_count) { - // Goodbye should have ttl of 0 - return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, - authority_count, additional, additional_count, - MDNS_CLASS_IN, 0); -} - -static inline mdns_string_t -mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, - char* strbuffer, size_t capacity) { - // PTR record is just a string - if ((size >= offset + length) && (length >= 2)) - return mdns_string_extract(buffer, size, &offset, strbuffer, capacity); - mdns_string_t empty = {0, 0}; - return empty; -} - -static inline mdns_record_srv_t -mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, - char* strbuffer, size_t capacity) { - mdns_record_srv_t srv; - memset(&srv, 0, sizeof(mdns_record_srv_t)); - // Read the service priority, weight, port number and the discovery name - // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): - // 2 bytes network-order unsigned priority - // 2 bytes network-order unsigned weight - // 2 bytes network-order unsigned port - // string: discovery (domain) name, minimum 2 bytes when compressed - if ((size >= offset + length) && (length >= 8)) { - const uint16_t* recorddata = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); - srv.priority = mdns_ntohs(recorddata++); - srv.weight = mdns_ntohs(recorddata++); - srv.port = mdns_ntohs(recorddata++); - offset += 6; - srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity); - } - return srv; -} - -static inline struct sockaddr_in* -mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, - struct sockaddr_in* addr) { - memset(addr, 0, sizeof(struct sockaddr_in)); - addr->sin_family = AF_INET; -#ifdef __APPLE__ - addr->sin_len = sizeof(struct sockaddr_in); -#endif - if ((size >= offset + length) && (length == 4)) - memcpy(&addr->sin_addr.s_addr, MDNS_POINTER_OFFSET(buffer, offset), 4); - return addr; -} - -static inline struct sockaddr_in6* -mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, - struct sockaddr_in6* addr) { - memset(addr, 0, sizeof(struct sockaddr_in6)); - addr->sin6_family = AF_INET6; -#ifdef __APPLE__ - addr->sin6_len = sizeof(struct sockaddr_in6); -#endif - if ((size >= offset + length) && (length == 16)) - memcpy(&addr->sin6_addr, MDNS_POINTER_OFFSET(buffer, offset), 16); - return addr; -} - -static inline size_t -mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, - mdns_record_txt_t* records, size_t capacity) { - size_t parsed = 0; - const char* strdata; - size_t end = offset + length; - - if (size < end) - end = size; - - while ((offset < end) && (parsed < capacity)) { - strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset); - size_t sublength = *(const unsigned char*)strdata; - - ++strdata; - offset += sublength + 1; - - size_t separator = 0; - for (size_t c = 0; c < sublength; ++c) { - // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] - if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) - break; - if (strdata[c] == '=') { - separator = c; - break; - } - } - - if (!separator) - continue; - - if (separator < sublength) { - records[parsed].key.str = strdata; - records[parsed].key.length = separator; - records[parsed].value.str = strdata + separator + 1; - records[parsed].value.length = sublength - (separator + 1); - } else { - records[parsed].key.str = strdata; - records[parsed].key.length = sublength; - } - - ++parsed; - } - - return parsed; -} - -#ifdef _WIN32 -#undef strncasecmp -#endif - -#ifdef __cplusplus -} -#endif
commit ae276b5cf0611c26e8d07e30b1e20c3a26d65fdd Author: Vasiliy Glazov vascom2@gmail.com Date: Thu Mar 2 15:59:15 2023 +0300
Fix require LUT files.
diff --git a/hyperhdr.spec b/hyperhdr.spec index e959b7c..d8c4e66 100644 --- a/hyperhdr.spec +++ b/hyperhdr.spec @@ -28,6 +28,7 @@ BuildRequires: systemd-rpm-macros BuildRequires: desktop-file-utils
Requires: hicolor-icon-theme +Requires: %{name}-common
Provides: bundled(mdns) = 1.4.2
commit ce0173c1f4a80326bbd7e6f7d53c6fd97c5e3e36 Author: Vasiliy Glazov vascom2@gmail.com Date: Thu Mar 2 15:51:50 2023 +0300
Initial commit.
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..db649b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/hyperhdr-19.0.0.0.tar.gz diff --git a/fix.patch b/fix.patch new file mode 100644 index 0000000..2b97587 --- /dev/null +++ b/fix.patch @@ -0,0 +1,393 @@ +diff -uNdr HyperHDR-19.0.0.0_orig/cmake/debian/service/hyperhdr.systemd HyperHDR-19.0.0.0_new/cmake/debian/service/hyperhdr.systemd +--- HyperHDR-19.0.0.0_orig/cmake/debian/service/hyperhdr.systemd 2023-02-12 20:20:03.000000000 +0300 ++++ HyperHDR-19.0.0.0_new/cmake/debian/service/hyperhdr.systemd 2023-03-02 10:57:51.400963373 +0300 +@@ -1,15 +1,16 @@ + [Unit] +-Description=HyperHdr ambient light systemd service for user %i ++Description=HyperHdr ambient light systemd service + After=network.target + + [Service] + ExecStart=/usr/bin/hyperhdr +-WorkingDirectory=/usr/share/hyperhdr/bin +-User=%i + TimeoutStopSec=5 + KillMode=mixed + Restart=on-failure + RestartSec=2 ++NoNewPrivileges=yes ++ProtectControlGroups=yes ++RestrictNamespaces=yes + + [Install] +-WantedBy=multi-user.target ++WantedBy=default.target +diff -uNdr HyperHDR-19.0.0.0_orig/cmake/Dependencies.cmake HyperHDR-19.0.0.0_new/cmake/Dependencies.cmake +--- HyperHDR-19.0.0.0_orig/cmake/Dependencies.cmake 2023-02-12 20:20:03.000000000 +0300 ++++ HyperHDR-19.0.0.0_new/cmake/Dependencies.cmake 2023-03-02 11:12:49.854396460 +0300 +@@ -234,10 +234,10 @@ + if (LIBSMARTX11) + SET(resolved_file ${LIBSMARTX11}) + get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) ++ gp_append_unique(PREREQUISITE_LIBSZ ${resolved_file}) + message(STATUS "Adding smartX11: ${resolved_file}") + get_filename_component(file_canonical ${resolved_file} REALPATH) +- gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) ++ gp_append_unique(PREREQUISITE_LIBSZ ${file_canonical}) + message(STATUS "Added smartX11(2): ${file_canonical}") + endif() + +@@ -250,10 +250,10 @@ + if (LIBSMARTPIPEWIRE) + SET(resolved_file ${LIBSMARTPIPEWIRE}) + get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) ++ gp_append_unique(PREREQUISITE_LIBSZ ${resolved_file}) + message(STATUS "Adding smartPipewire: ${resolved_file}") + get_filename_component(file_canonical ${resolved_file} REALPATH) +- gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) ++ gp_append_unique(PREREQUISITE_LIBSZ ${file_canonical}) + message(STATUS "Added smartPipewire(2): ${file_canonical}") + endif() + +@@ -266,16 +266,16 @@ + if (LIBSMARTCEC) + SET(resolved_file ${LIBSMARTCEC}) + get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) ++ gp_append_unique(PREREQUISITE_LIBSZ ${resolved_file}) + message(STATUS "Adding CEC: ${resolved_file}") + set(resolved_file "${resolved_file}.6") + if(EXISTS ${resolved_file}) + get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) ++ gp_append_unique(PREREQUISITE_LIBSZ ${resolved_file}) + message(STATUS "Adding CEC(2): ${resolved_file}") + endif() + get_filename_component(file_canonical ${resolved_file} REALPATH) +- gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) ++ gp_append_unique(PREREQUISITE_LIBSZ ${file_canonical}) + message(STATUS "Added CEC(3): ${file_canonical}") + endif() + +@@ -328,143 +328,18 @@ + message(STATUS "Added CEC support: ${file_canonical}") + endif() + +- if ( GLD ) +- SET(resolved_file ${GLD}) +- message(STATUS "Adding GLD: ${resolved_file}") +- get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) +- message(STATUS "Added GLD: ${resolved_file}") +- set(resolved_file0 "${resolved_file}.0") +- if(EXISTS ${resolved_file0}) +- message(STATUS "Adding GLD0: ${resolved_file0}") +- get_filename_component(resolved_file0 ${resolved_file0} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file0}) +- message(STATUS "Added GLD0: ${resolved_file0}") +- endif() +- get_filename_component(file_canonical ${resolved_file} REALPATH) +- gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) +- message(STATUS "Added GLD: ${file_canonical}") +- endif() +- +- +- find_library(LIB_XCB +- NAMES libxcb libxcb.so +- ) +- +- if(LIB_XCB) +- message(STATUS "libXCB found ${LIB_XCB}") +- SET(resolved_file ${LIB_XCB}) +- message(STATUS "Adding libXCB: ${resolved_file}") +- get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) +- message(STATUS "Added libXCB: ${resolved_file}") +- set(resolved_file1 "${resolved_file}.1") +- if(EXISTS ${resolved_file1}) +- message(STATUS "Adding libXCB1: ${resolved_file1}") +- get_filename_component(resolved_file1 ${resolved_file1} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file1}) +- message(STATUS "Added libXCB1: ${resolved_file1}") +- endif() +- get_filename_component(file_canonical ${resolved_file} REALPATH) +- gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) +- message(STATUS "Added: ${file_canonical}") +- else() +- message(STATUS "libXCB not found") +- endif() +- +- find_library(LIB_GLX +- NAMES libGLX libGLX.so +- ) +- +- if(LIB_GLX) +- message(STATUS "libGLX found ${LIB_GLX}") +- SET(resolved_file ${LIB_GLX}) +- message(STATUS "Adding LIB_GLX: ${resolved_file}") +- get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) +- message(STATUS "Added LIB_GLX: ${resolved_file}") +- set(resolved_file0 "${resolved_file}.0") +- if(EXISTS ${resolved_file0}) +- message(STATUS "Adding LIB_GLX0: ${resolved_file0}") +- get_filename_component(resolved_file0 ${resolved_file0} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file0}) +- message(STATUS "Added LIB_GLX0: ${resolved_file0}") +- endif() +- get_filename_component(file_canonical ${resolved_file} REALPATH) +- gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) +- message(STATUS "Added: ${file_canonical}") +- else() +- message(STATUS "libGLX not found") +- endif() + +- # Copy Qt plugins to 'share/hyperhdr/lib' +- if(QT_PLUGINS_DIR) +- foreach(PLUGIN "platforms" "sqldrivers" "imageformats") +- if(EXISTS ${QT_PLUGINS_DIR}/${PLUGIN}) +- file(GLOB files "${QT_PLUGINS_DIR}/${PLUGIN}/*") +- foreach(file ${files}) +- if (NOT CMAKE_CROSSCOMPILING) +- get_prerequisites(${file} PLUGINS 0 1 "" "") +- endif() +- +- foreach(DEPENDENCY ${PLUGINS}) +- get_filename_component(resolved ${DEPENDENCY} NAME_WE) +- +- foreach(myitem ${SYSTEM_LIBS_SKIP}) +- #message(STATUS "Checking ${myitem}") +- string(FIND ${myitem} ${resolved} _index) +- if (${_index} GREATER -1) +- #message(STATUS "${myitem} = ${resolved}") +- break() +- endif() +- endforeach() +- +- if (${_index} GREATER -1) +- #message(STATUS "QT skipped: ${resolved}") +- continue() # Skip system libraries +- else() +- #message(STATUS "QT included: ${resolved}") +- gp_resolve_item("${file}" "${DEPENDENCY}" "" "" resolved_file) +- get_filename_component(resolved_file ${resolved_file} ABSOLUTE) +- gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) +- get_filename_component(file_canonical ${resolved_file} REALPATH) +- gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) +- #message(STATUS "QT added: ${resolved_file}") +- endif() +- endforeach() +- +- install( +- FILES ${file} +- DESTINATION "share/hyperhdr/lib/${PLUGIN}" +- COMPONENT "HyperHDR" +- ) +- endforeach() +- endif() +- endforeach() +- endif(QT_PLUGINS_DIR) +- +- # Create a qt.conf file in 'share/hyperhdr/bin' to override hard-coded search paths in Qt plugins +- file(WRITE "${CMAKE_BINARY_DIR}/qt.conf" "[Paths]\nPlugins=../lib/\n") +- install( +- FILES "${CMAKE_BINARY_DIR}/qt.conf" +- DESTINATION "share/hyperhdr/bin" +- COMPONENT "HyperHDR" +- ) +- +- # Copy dependencies to 'share/hyperhdr/lib' +- foreach(PREREQUISITE_LIB ${PREREQUISITE_LIBS}) +- message("Installing: " ${PREREQUISITE_LIB}) ++ # Copy dependencies to '' ++ foreach(PREREQUISITE_LIB ${PREREQUISITE_LIBSZ}) ++ message("Installing: " ${PREREQUISITE_LIBZ}) + install( +- FILES ${PREREQUISITE_LIB} +- DESTINATION "share/hyperhdr/lib" ++ PROGRAMS ${PREREQUISITE_LIB} ++ DESTINATION "lib64" + COMPONENT "HyperHDR" + ) + endforeach() + +- # install LUT +- install(FILES "${PROJECT_SOURCE_DIR}/resources/lut/lut_lin_tables.tar.xz" DESTINATION "share/hyperhdr/lut" COMPONENT "HyperHDR") +- install(FILES "${PROJECT_SOURCE_DIR}/LICENSE" DESTINATION "share/hyperhdr" COMPONENT "HyperHDR") +- install(FILES "${PROJECT_SOURCE_DIR}/3RD_PARTY_LICENSES" DESTINATION "share/hyperhdr" COMPONENT "HyperHDR") ++ + else() + # Run CMake after target was built to run get_prerequisites on ${TARGET_FILE} + add_custom_command( +@@ -504,82 +379,6 @@ + separate_arguments(DEPENDENCIES WINDOWS_COMMAND ${DEPS}) + string(REPLACE "\" "/" DEPENDENCIES "${DEPENDENCIES}") + +- # Copy dependencies to 'hyperhdr/lib' or 'hyperhdr' +- while (DEPENDENCIES) +- list(GET DEPENDENCIES 0 src) +- list(GET DEPENDENCIES 1 dst) +- get_filename_component(dst ${dst} DIRECTORY) +- +- if (NOT "${dst}" STREQUAL "") +- install( +- FILES ${src} +- DESTINATION "lib/${dst}" +- COMPONENT "HyperHDR" +- ) +- else() +- install( +- FILES ${src} +- DESTINATION "bin" +- COMPONENT "HyperHDR" +- ) +- endif() +- +- list(REMOVE_AT DEPENDENCIES 0 1) +- endwhile() +- +- # Copy TurboJPEG Libs +- if (ENABLE_MF) +- find_file(TurboJPEG_DLL +- NAMES "turbojpeg.dll" "jpeg62.dll" +- PATHS "${TURBOJPEG_LIBRARY_DIRS}" +- NO_DEFAULT_PATH +- REQUIRED +- ) +- +- if(NOT CMAKE_GITHUB_ACTION) +- get_filename_component(JPEG_RUNTIME_TARGET ${TARGET_FILE} DIRECTORY) +- execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${TurboJPEG_DLL} ${JPEG_RUNTIME_TARGET}) +- endif() +- +- install( +- FILES ${TurboJPEG_DLL} +- DESTINATION "bin" +- COMPONENT "HyperHDR" +- ) +- endif() +- +- # Copy MQTT Libs +- if (ENABLE_MQTT) +- set (MQTT_TARGET_LIB_FOLDER ${LIBRARY_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}) +- message(${MQTT_TARGET_LIB_FOLDER}) +- find_file(MQTT_DLL +- NAMES "qmqtt.dll" +- PATHS "${MQTT_TARGET_LIB_FOLDER}" +- NO_DEFAULT_PATH +- REQUIRED +- ) +- message(${MQTT_DLL}) +- if(NOT CMAKE_GITHUB_ACTION) +- get_filename_component(MQTT_RUNTIME_TARGET ${TARGET_FILE} DIRECTORY) +- execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${MQTT_DLL} ${MQTT_RUNTIME_TARGET}) +- endif() +- +- install( +- FILES ${MQTT_DLL} +- DESTINATION "bin" +- COMPONENT "HyperHDR" +- ) +- endif() +- +- +- # Create a qt.conf file in 'bin' to override hard-coded search paths in Qt plugins +- file(WRITE "${CMAKE_BINARY_DIR}/qt.conf" "[Paths]\nPlugins=../lib/\n") +- install( +- FILES "${CMAKE_BINARY_DIR}/qt.conf" +- DESTINATION "bin" +- COMPONENT "HyperHDR" +- ) +- + execute_process( + COMMAND ${SEVENZIP_BIN} e ${PROJECT_SOURCE_DIR}/resources/lut/lut_lin_tables.tar.xz -o${CMAKE_CURRENT_BINARY_DIR} -aoa -y + RESULT_VARIABLE STATUS +diff -uNdr HyperHDR-19.0.0.0_orig/cmake/desktop/hyperhdr.desktop HyperHDR-19.0.0.0_new/cmake/desktop/hyperhdr.desktop +--- HyperHDR-19.0.0.0_orig/cmake/desktop/hyperhdr.desktop 2023-02-12 20:20:03.000000000 +0300 ++++ HyperHDR-19.0.0.0_new/cmake/desktop/hyperhdr.desktop 2023-03-02 10:58:29.233908007 +0300 +@@ -2,7 +2,7 @@ + Name=HyperHDR + GenericName=HyperHDR Ambient Lighting + Comment=HyperHDR mimics the well known Ambilight from Philips +-Icon=/usr/share/pixmaps/hyperhdr/hyperhdr_128.png ++Icon=hyperhdr + Terminal=false + TryExec=hyperhdr + Exec=hyperhdr +diff -uNdr HyperHDR-19.0.0.0_orig/CMakeLists.txt HyperHDR-19.0.0.0_new/CMakeLists.txt +--- HyperHDR-19.0.0.0_orig/CMakeLists.txt 2023-02-12 20:20:03.000000000 +0300 ++++ HyperHDR-19.0.0.0_new/CMakeLists.txt 2023-03-02 10:46:32.032000510 +0300 +@@ -445,14 +445,6 @@ + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-new-dtags") + endif() + +-# setup -rpath to search for shared libs in BINARY/../lib folder +-if (UNIX AND NOT APPLE) +- SET(CMAKE_SKIP_BUILD_RPATH FALSE) +- SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +- SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:$ORIGIN/../lib") +- SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +-endif () +- + # find Threads libs + find_package(Threads REQUIRED) + +diff -uNdr HyperHDR-19.0.0.0_orig/sources/hyperhdr/CMakeLists.txt HyperHDR-19.0.0.0_new/sources/hyperhdr/CMakeLists.txt +--- HyperHDR-19.0.0.0_orig/sources/hyperhdr/CMakeLists.txt 2023-02-12 20:20:03.000000000 +0300 ++++ HyperHDR-19.0.0.0_new/sources/hyperhdr/CMakeLists.txt 2023-03-02 11:03:15.187047845 +0300 +@@ -130,13 +130,13 @@ + if(APPLE) + install ( TARGETS hyperhdr DESTINATION "share/.." COMPONENT "HyperHDR" ) + elseif(NOT WIN32) +- install ( TARGETS hyperhdr DESTINATION "share/hyperhdr/bin" COMPONENT "HyperHDR" ) +- install ( DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/debian/service DESTINATION "share/hyperhdr/" COMPONENT "HyperHDR" ) +- install ( FILES ${CMAKE_SOURCE_DIR}/resources/icons/hyperhdr-icon-32px.png DESTINATION "share/hyperhdr/icons" COMPONENT "HyperHDR" ) ++ install ( TARGETS hyperhdr DESTINATION "bin" COMPONENT "HyperHDR" ) ++ install ( FILES ${CMAKE_SOURCE_DIR}/resources/icons/hyperhdr-icon-32px.png DESTINATION "share/icons/hicolor/32x32/apps" RENAME "hyperhdr.png" COMPONENT "HyperHDR" ) ++ install ( FILES ${CMAKE_SOURCE_DIR}/cmake/debian/service/hyperhdr.systemd DESTINATION "lib/systemd/user" RENAME "hyperhdr.service" COMPONENT "HyperHDR" ) + + # Desktop file for hyperhdr +- install ( FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperhdr_128.png DESTINATION "share/hyperhdr/desktop" COMPONENT "HyperHDR" ) +- install ( FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperhdr.desktop DESTINATION "share/hyperhdr/desktop" COMPONENT "HyperHDR" ) ++ install ( FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperhdr_128.png DESTINATION "share/icons/hicolor/128x128/apps" RENAME "hyperhdr.png" COMPONENT "HyperHDR" ) ++ install ( FILES ${CMAKE_SOURCE_DIR}/cmake/desktop/hyperhdr.desktop DESTINATION "share/applications" COMPONENT "HyperHDR" ) + else() + install ( TARGETS hyperhdr DESTINATION "bin" COMPONENT "HyperHDR" ) + +@@ -145,12 +145,6 @@ + include( InstallRequiredSystemLibraries ) + endif() + +-if(CMAKE_HOST_UNIX AND NOT APPLE) +- install( CODE "EXECUTE_PROCESS(COMMAND ln -sf "../share/hyperhdr/bin/hyperhdr" "${CMAKE_BINARY_DIR}/symlink_hyperhdr" )" COMPONENT "HyperHDR" ) +- install( FILES ${CMAKE_BINARY_DIR}/symlink_hyperhdr DESTINATION "bin" RENAME hyperhdr COMPONENT "HyperHDR" ) +- install( CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_hyperhdr )" COMPONENT "HyperHDR" ) +-endif() +- + # Copy dependencies (not for OSX) + include(${CMAKE_SOURCE_DIR}/cmake/Dependencies.cmake) + +diff -uNdr HyperHDR-19.0.0.0_orig/sources/hyperhdr-remote/CMakeLists.txt HyperHDR-19.0.0.0_new/sources/hyperhdr-remote/CMakeLists.txt +--- HyperHDR-19.0.0.0_orig/sources/hyperhdr-remote/CMakeLists.txt 2023-02-12 20:20:03.000000000 +0300 ++++ HyperHDR-19.0.0.0_new/sources/hyperhdr-remote/CMakeLists.txt 2023-03-02 11:00:37.892120417 +0300 +@@ -44,17 +44,7 @@ + Qt${Qt_VERSION}::Core + Qt${Qt_VERSION}::Network) + +-if(NOT WIN32) +- install ( TARGETS ${PROJECT_NAME} DESTINATION "share/hyperhdr/bin" COMPONENT "HyperHDR_remote" ) +-else() +- install ( TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "HyperHDR_remote" ) +-endif() +- +-if(CMAKE_HOST_UNIX) +- install(CODE "EXECUTE_PROCESS(COMMAND ln -sf "../share/hyperhdr/bin/${PROJECT_NAME}" "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" )" COMPONENT "HyperHDR_remote" ) +- install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "HyperHDR_remote" ) +- install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "HyperHDR_remote" ) +-endif(CMAKE_HOST_UNIX) ++install ( TARGETS ${PROJECT_NAME} DESTINATION "bin" COMPONENT "HyperHDR_remote" ) + + if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) + target_precompile_headers(${PROJECT_NAME} REUSE_FROM precompiled_hyperhdr_headers) diff --git a/hyperhdr.spec b/hyperhdr.spec new file mode 100644 index 0000000..e959b7c --- /dev/null +++ b/hyperhdr.spec @@ -0,0 +1,102 @@ +Name: hyperhdr +Version: 19.0.0.0 +Release: 2%{?dist} +Summary: Ambient lighting + +License: MIT AND LicenseRef-Fedora-Public-Domain AND Apache-2.0 AND BSL-1.0 AND BSD-3-Clause +URL: https://github.com/awawa-dev/HyperHDR +Source0: %{url}/archive/refs/tags/v%{version}/%{name}-%{version}.tar.gz +Source1: https://raw.githubusercontent.com/mjansson/mdns/1.4.2/mdns.h +Patch0: fix.patch + +# https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval +ExcludeArch: %{ix86} + +BuildRequires: gcc-c++ +BuildRequires: ninja-build +BuildRequires: pkgconfig +BuildRequires: cmake +BuildRequires: cmake(Qt6) +BuildRequires: pkgconfig(libturbojpeg) +BuildRequires: pkgconfig(alsa) +BuildRequires: cmake(Qt6SerialPort) +BuildRequires: pkgconfig(libpipewire-0.3) +BuildRequires: mbedtls-devel +BuildRequires: cmake(flatbuffers) +BuildRequires: flatbuffers-compiler +BuildRequires: systemd-rpm-macros +BuildRequires: desktop-file-utils + +Requires: hicolor-icon-theme + +Provides: bundled(mdns) = 1.4.2 + +%description +Open source ambient lighting implementation for television sets based on the +video and audio streams analysis, using performance improvements especially +for USB grabbers. + +%package common +Summary: LUT files for %{name} +Requires: %{name} = %{version}-%{release} +BuildArch: noarch + +%description common +The %{name}-common package contains LUT files for +%{name}. + +%prep +%autosetup -p1 -n HyperHDR-%{version} + +mkdir dependencies/bonjour +cp %{SOURCE1} dependencies/bonjour/ +sed -i -e 's|file(DOWNLOAD "https://raw.githubusercontent.com/mjansson/mdns/$%7BMJANSSON_MDNS_VERSION%7D...' \ + -e 's|"${CMAKE_SOURCE_DIR}/dependencies/bonjour/mdns.h"||' \ + -e 's|STATUS MJANSSON_MDNS_STATUS_H)||' CMakeLists.txt + +%cmake -G Ninja \ + -DCMAKE_CXX_STANDARD=17 \ + -DUSE_SYSTEM_FLATBUFFERS_LIBS:BOOL=ON \ + -DUSE_SYSTEM_MBEDTLS_LIBS:BOOL=ON \ + -DENABLE_MQTT:BOOL=OFF \ + -DBUILD_SHARED_LIBS:BOOL=OFF \ + -DPLATFORM=linux + +%build +%cmake_build + + +%install +%cmake_install +mkdir -p %{buildroot}%{_datadir}/%{name}/lut/ +tar -xf resources/lut/lut_lin_tables.tar.xz -C %{buildroot}%{_datadir}/%{name}/lut/ + +%check +desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop + +%post +%systemd_user_post %{name}.service + +%preun +%systemd_user_preun %{name}.service + +%files +%license LICENSE +%doc README.md +%{_bindir}/%{name} +%{_bindir}/%{name}-remote +%{_libdir}/libsmart*.so* +%{_userunitdir}/%{name}.service +%{_datadir}/applications/*.desktop +%{_datadir}/icons/hicolor/*/apps/*.png + +%files common +%{_datadir}/%{name} + +%changelog +* Thu Mar 02 2023 Vasiliy Glazov vascom2@gmail.com - 19.0.0.0-2 +- Drop i686 builds +- Add bundled provides + +* Tue Feb 28 2023 Vasiliy Glazov vascom2@gmail.com - 19.0.0.0-1 +- Initial packaging for Fedora diff --git a/mdns.h b/mdns.h new file mode 100644 index 0000000..4b3b03d --- /dev/null +++ b/mdns.h @@ -0,0 +1,1614 @@ +/* mdns.h - mDNS/DNS-SD library - Public Domain - 2017 Mattias Jansson + * + * This library provides a cross-platform mDNS and DNS-SD library in C. + * The implementation is based on RFC 6762 and RFC 6763. + * + * The latest source code is always available at + * + * https://github.com/mjansson/mdns + * + * This library is put in the public domain; you can redistribute it and/or modify it without any + * restrictions. + * + */ + +#pragma once + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <fcntl.h> +#ifdef _WIN32 +#include <Winsock2.h> +#include <Ws2tcpip.h> +#define strncasecmp _strnicmp +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define MDNS_INVALID_POS ((size_t)-1) + +#define MDNS_STRING_CONST(s) (s), (sizeof((s)) - 1) +#define MDNS_STRING_ARGS(s) s.str, s.length +#define MDNS_STRING_FORMAT(s) (int)((s).length), s.str + +#define MDNS_POINTER_OFFSET(p, ofs) ((void*)((char*)(p) + (ptrdiff_t)(ofs))) +#define MDNS_POINTER_OFFSET_CONST(p, ofs) ((const void*)((const char*)(p) + (ptrdiff_t)(ofs))) +#define MDNS_POINTER_DIFF(a, b) ((size_t)((const char*)(a) - (const char*)(b))) + +#define MDNS_PORT 5353 +#define MDNS_UNICAST_RESPONSE 0x8000U +#define MDNS_CACHE_FLUSH 0x8000U +#define MDNS_MAX_SUBSTRINGS 64 + +enum mdns_record_type { + MDNS_RECORDTYPE_IGNORE = 0, + // Address + MDNS_RECORDTYPE_A = 1, + // Domain Name pointer + MDNS_RECORDTYPE_PTR = 12, + // Arbitrary text string + MDNS_RECORDTYPE_TXT = 16, + // IP6 Address [Thomson] + MDNS_RECORDTYPE_AAAA = 28, + // Server Selection [RFC2782] + MDNS_RECORDTYPE_SRV = 33, + // Any available records + MDNS_RECORDTYPE_ANY = 255 +}; + +enum mdns_entry_type { + MDNS_ENTRYTYPE_QUESTION = 0, + MDNS_ENTRYTYPE_ANSWER = 1, + MDNS_ENTRYTYPE_AUTHORITY = 2, + MDNS_ENTRYTYPE_ADDITIONAL = 3 +}; + +enum mdns_class { MDNS_CLASS_IN = 1, MDNS_CLASS_ANY = 255 }; + +typedef enum mdns_record_type mdns_record_type_t; +typedef enum mdns_entry_type mdns_entry_type_t; +typedef enum mdns_class mdns_class_t; + +typedef int (*mdns_record_callback_fn)(int sock, const struct sockaddr* from, size_t addrlen, + mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype, + uint16_t rclass, uint32_t ttl, const void* data, size_t size, + size_t name_offset, size_t name_length, size_t record_offset, + size_t record_length, void* user_data); + +typedef struct mdns_string_t mdns_string_t; +typedef struct mdns_string_pair_t mdns_string_pair_t; +typedef struct mdns_string_table_item_t mdns_string_table_item_t; +typedef struct mdns_string_table_t mdns_string_table_t; +typedef struct mdns_record_t mdns_record_t; +typedef struct mdns_record_srv_t mdns_record_srv_t; +typedef struct mdns_record_ptr_t mdns_record_ptr_t; +typedef struct mdns_record_a_t mdns_record_a_t; +typedef struct mdns_record_aaaa_t mdns_record_aaaa_t; +typedef struct mdns_record_txt_t mdns_record_txt_t; +typedef struct mdns_query_t mdns_query_t; + +#ifdef _WIN32 +typedef int mdns_size_t; +typedef int mdns_ssize_t; +#else +typedef size_t mdns_size_t; +typedef ssize_t mdns_ssize_t; +#endif + +struct mdns_string_t { + const char* str; + size_t length; +}; + +struct mdns_string_pair_t { + size_t offset; + size_t length; + int ref; +}; + +struct mdns_string_table_t { + size_t offset[16]; + size_t count; + size_t next; +}; + +struct mdns_record_srv_t { + uint16_t priority; + uint16_t weight; + uint16_t port; + mdns_string_t name; +}; + +struct mdns_record_ptr_t { + mdns_string_t name; +}; + +struct mdns_record_a_t { + struct sockaddr_in addr; +}; + +struct mdns_record_aaaa_t { + struct sockaddr_in6 addr; +}; + +struct mdns_record_txt_t { + mdns_string_t key; + mdns_string_t value; +}; + +struct mdns_record_t { + mdns_string_t name; + mdns_record_type_t type; + union mdns_record_data { + mdns_record_ptr_t ptr; + mdns_record_srv_t srv; + mdns_record_a_t a; + mdns_record_aaaa_t aaaa; + mdns_record_txt_t txt; + } data; + uint16_t rclass; + uint32_t ttl; +}; + +struct mdns_header_t { + uint16_t query_id; + uint16_t flags; + uint16_t questions; + uint16_t answer_rrs; + uint16_t authority_rrs; + uint16_t additional_rrs; +}; + +struct mdns_query_t { + mdns_record_type_t type; + const char* name; + size_t length; +}; + +// mDNS/DNS-SD public API + +//! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass +//! in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. To +//! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. +static inline int +mdns_socket_open_ipv4(const struct sockaddr_in* saddr); + +//! Setup an already opened IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, +//! pass in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. +//! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. +static inline int +mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr); + +//! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass +//! in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. To +//! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. +static inline int +mdns_socket_open_ipv6(const struct sockaddr_in6* saddr); + +//! Setup an already opened IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, +//! pass in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. +//! To send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a +//! random user level ephemeral port. To run discovery service listening for incoming discoveries +//! and queries, you must set MDNS_PORT as port. +static inline int +mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr); + +//! Close a socket opened with mdns_socket_open_ipv4 and mdns_socket_open_ipv6. +static inline void +mdns_socket_close(int sock); + +//! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been opened +//! on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be 32 bit +//! aligned. Parsing is stopped when callback function returns non-zero. Returns the number of +//! queries parsed. +static inline size_t +mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data); + +//! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns 0 +//! on success, or <0 if error. +static inline int +mdns_discovery_send(int sock); + +//! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to +//! the given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback +//! function returns non-zero. Returns the number of responses parsed. +static inline size_t +mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data); + +//! Send a multicast mDNS query on the given socket for the given service name. The supplied buffer +//! will be used to build the query packet and must be 32 bit aligned. The query ID can be set to +//! non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for +//! multicast queries. The query will request a unicast response if the socket is bound to an +//! ephemeral port, or a multicast response if the socket is bound to mDNS port 5353. Returns the +//! used query ID, or <0 if error. +static inline int +mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, + size_t capacity, uint16_t query_id); + +//! Send a multicast mDNS query on the given socket for the given service names. The supplied buffer +//! will be used to build the query packet and must be 32 bit aligned. The query ID can be set to +//! non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for +//! multicast queries. Each additional service name query consists of a triplet - a record type +//! (mdns_record_type_t), a name string pointer (const char*) and a name length (size_t). The list +//! of variable arguments should be terminated with a record type of 0. The query will request a +//! unicast response if the socket is bound to an ephemeral port, or a multicast response if the +//! socket is bound to mDNS port 5353. Returns the used query ID, or <0 if error. +static inline int +mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, + size_t capacity, uint16_t query_id); + +//! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering +//! out any responses not matching the given query ID. Set the query ID to 0 to parse all responses, +//! even if it is not matching the query ID set in a specific query. Any data will be piped to the +//! given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback +//! function returns non-zero. Returns the number of responses parsed. +static inline size_t +mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data, int query_id); + +//! Send a variable unicast mDNS query answer to any question with variable number of records to the +//! given address. Use the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query +//! recieved to determine if the answer should be sent unicast (bit set) or multicast (bit not set). +//! Buffer must be 32 bit aligned. The record type and name should match the data from the query +//! recieved. Returns 0 if success, or <0 if error. +static inline int +mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, + size_t capacity, uint16_t query_id, mdns_record_type_t record_type, + const char* name, size_t name_length, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); + +//! Send a variable multicast mDNS query answer to any question with variable number of records. Use +//! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine +//! if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be 32 bit +//! aligned. Returns 0 if success, or <0 if error. +static inline int +mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); + +//! Send a variable multicast mDNS announcement (as an unsolicited answer) with variable number of +//! records.Buffer must be 32 bit aligned. Returns 0 if success, or <0 if error. Use this on service +//! startup to announce your instance to the local network. +static inline int +mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); + +//! Send a variable multicast mDNS announcement. Use this on service end for removing the resource +//! from the local network. The records must be identical to the according announcement. +static inline int +mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); + +// Parse records functions + +//! Parse a PTR record, returns the name in the record +static inline mdns_string_t +mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity); + +//! Parse a SRV record, returns the priority, weight, port and name in the record +static inline mdns_record_srv_t +mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity); + +//! Parse an A record, returns the IPv4 address in the record +static inline struct sockaddr_in* +mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in* addr); + +//! Parse an AAAA record, returns the IPv6 address in the record +static inline struct sockaddr_in6* +mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in6* addr); + +//! Parse a TXT record, returns the number of key=value records parsed and stores the key-value +//! pairs in the supplied buffer +static inline size_t +mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, + mdns_record_txt_t* records, size_t capacity); + +// Internal functions + +static inline mdns_string_t +mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity); + +static inline int +mdns_string_skip(const void* buffer, size_t size, size_t* offset); + +static inline size_t +mdns_string_find(const char* str, size_t length, char c, size_t offset); + +//! Compare if two strings are equal. If the strings are equal it returns >0 and the offset variables are +//! updated to the end of the corresponding strings. If the strings are not equal it returns 0 and +//! the offset variables are NOT updated. +static inline int +mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs, + size_t size_rhs, size_t* ofs_rhs); + +static inline void* +mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, + mdns_string_table_t* string_table); + +static inline size_t +mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, + const char* str, size_t first_length, size_t total_length); + +// Implementations + +static inline uint16_t +mdns_ntohs(const void* data) { + uint16_t aligned; + memcpy(&aligned, data, sizeof(uint16_t)); + return ntohs(aligned); +} + +static inline uint32_t +mdns_ntohl(const void* data) { + uint32_t aligned; + memcpy(&aligned, data, sizeof(uint32_t)); + return ntohl(aligned); +} + +static inline void* +mdns_htons(void* data, uint16_t val) { + val = htons(val); + memcpy(data, &val, sizeof(uint16_t)); + return MDNS_POINTER_OFFSET(data, sizeof(uint16_t)); +} + +static inline void* +mdns_htonl(void* data, uint32_t val) { + val = htonl(val); + memcpy(data, &val, sizeof(uint32_t)); + return MDNS_POINTER_OFFSET(data, sizeof(uint32_t)); +} + +static inline int +mdns_socket_open_ipv4(const struct sockaddr_in* saddr) { + int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv4(sock, saddr)) { + mdns_socket_close(sock); + return -1; + } + return sock; +} + +static inline int +mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) { + unsigned char ttl = 1; + unsigned char loopback = 1; + unsigned int reuseaddr = 1; + struct ip_mreq req; + + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); +#endif + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)); + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); + + memset(&req, 0, sizeof(req)); + req.imr_multiaddr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); + if (saddr) + req.imr_interface = saddr->sin_addr; + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof(req))) + return -1; + + struct sockaddr_in sock_addr; + if (!saddr) { + memset(&sock_addr, 0, sizeof(struct sockaddr_in)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = INADDR_ANY; +#ifdef __APPLE__ + sock_addr.sin_len = sizeof(struct sockaddr_in); +#endif + } else { + memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in)); + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&sock_addr.sin_addr, + sizeof(sock_addr.sin_addr)); +#ifndef _WIN32 + sock_addr.sin_addr.s_addr = INADDR_ANY; +#endif + } + + if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in))) + return -1; + +#ifdef _WIN32 + unsigned long param = 1; + ioctlsocket(sock, FIONBIO, ¶m); +#else + const int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif + + return 0; +} + +static inline int +mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) { + int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv6(sock, saddr)) { + mdns_socket_close(sock); + return -1; + } + return sock; +} + +static inline int +mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) { + int hops = 1; + unsigned int loopback = 1; + unsigned int reuseaddr = 1; + struct ipv6_mreq req; + + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(reuseaddr)); +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuseaddr, sizeof(reuseaddr)); +#endif + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops)); + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); + + memset(&req, 0, sizeof(req)); + req.ipv6mr_multiaddr.s6_addr[0] = 0xFF; + req.ipv6mr_multiaddr.s6_addr[1] = 0x02; + req.ipv6mr_multiaddr.s6_addr[15] = 0xFB; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req))) + return -1; + + struct sockaddr_in6 sock_addr; + if (!saddr) { + memset(&sock_addr, 0, sizeof(struct sockaddr_in6)); + sock_addr.sin6_family = AF_INET6; + sock_addr.sin6_addr = in6addr_any; +#ifdef __APPLE__ + sock_addr.sin6_len = sizeof(struct sockaddr_in6); +#endif + } else { + memcpy(&sock_addr, saddr, sizeof(struct sockaddr_in6)); + unsigned int ifindex = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&ifindex, sizeof(ifindex)); +#ifndef _WIN32 + sock_addr.sin6_addr = in6addr_any; +#endif + } + + if (bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in6))) + return -1; + +#ifdef _WIN32 + unsigned long param = 1; + ioctlsocket(sock, FIONBIO, ¶m); +#else + const int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif + + return 0; +} + +static inline void +mdns_socket_close(int sock) { +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif +} + +static inline int +mdns_is_string_ref(uint8_t val) { + return (0xC0 == (val & 0xC0)); +} + +static inline mdns_string_pair_t +mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { + const uint8_t* buffer = (const uint8_t*)rawdata; + mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0}; + if (offset >= size) + return pair; + if (!buffer[offset]) { + pair.offset = offset; + return pair; + } + int recursion = 0; + while (mdns_is_string_ref(buffer[offset])) { + if (size < offset + 2) + return pair; + + offset = mdns_ntohs(MDNS_POINTER_OFFSET(buffer, offset)) & 0x3fff; + if (offset >= size) + return pair; + + pair.ref = 1; + if (++recursion > 16) + return pair; + } + + size_t length = (size_t)buffer[offset++]; + if (size < offset + length) + return pair; + + pair.offset = offset; + pair.length = length; + + return pair; +} + +static inline int +mdns_string_skip(const void* buffer, size_t size, size_t* offset) { + size_t cur = *offset; + mdns_string_pair_t substr; + unsigned int counter = 0; + do { + substr = mdns_get_next_substring(buffer, size, cur); + if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS)) + return 0; + if (substr.ref) { + *offset = cur + 2; + return 1; + } + cur = substr.offset + substr.length; + } while (substr.length); + + *offset = cur + 1; + return 1; +} + +static inline int +mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs, + size_t size_rhs, size_t* ofs_rhs) { + size_t lhs_cur = *ofs_lhs; + size_t rhs_cur = *ofs_rhs; + size_t lhs_end = MDNS_INVALID_POS; + size_t rhs_end = MDNS_INVALID_POS; + mdns_string_pair_t lhs_substr; + mdns_string_pair_t rhs_substr; + unsigned int counter = 0; + do { + lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur); + rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur); + if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS) || + (counter++ > MDNS_MAX_SUBSTRINGS)) + return 0; + if (lhs_substr.length != rhs_substr.length) + return 0; + if (strncasecmp((const char*)MDNS_POINTER_OFFSET_CONST(buffer_rhs, rhs_substr.offset), + (const char*)MDNS_POINTER_OFFSET_CONST(buffer_lhs, lhs_substr.offset), + rhs_substr.length)) + return 0; + if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS)) + lhs_end = lhs_cur + 2; + if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS)) + rhs_end = rhs_cur + 2; + lhs_cur = lhs_substr.offset + lhs_substr.length; + rhs_cur = rhs_substr.offset + rhs_substr.length; + } while (lhs_substr.length); + + if (lhs_end == MDNS_INVALID_POS) + lhs_end = lhs_cur + 1; + *ofs_lhs = lhs_end; + + if (rhs_end == MDNS_INVALID_POS) + rhs_end = rhs_cur + 1; + *ofs_rhs = rhs_end; + + return 1; +} + +static inline mdns_string_t +mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity) { + size_t cur = *offset; + size_t end = MDNS_INVALID_POS; + mdns_string_pair_t substr; + mdns_string_t result; + result.str = str; + result.length = 0; + char* dst = str; + unsigned int counter = 0; + size_t remain = capacity; + do { + substr = mdns_get_next_substring(buffer, size, cur); + if ((substr.offset == MDNS_INVALID_POS) || (counter++ > MDNS_MAX_SUBSTRINGS)) + return result; + if (substr.ref && (end == MDNS_INVALID_POS)) + end = cur + 2; + if (substr.length) { + size_t to_copy = (substr.length < remain) ? substr.length : remain; + memcpy(dst, (const char*)buffer + substr.offset, to_copy); + dst += to_copy; + remain -= to_copy; + if (remain) { + *dst++ = '.'; + --remain; + } + } + cur = substr.offset + substr.length; + } while (substr.length); + + if (end == MDNS_INVALID_POS) + end = cur + 1; + *offset = end; + + result.length = capacity - remain; + return result; +} + +static inline size_t +mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, + const char* str, size_t first_length, size_t total_length) { + if (!string_table) + return MDNS_INVALID_POS; + + for (size_t istr = 0; istr < string_table->count; ++istr) { + if (string_table->offset[istr] >= capacity) + continue; + size_t offset = 0; + mdns_string_pair_t sub_string = + mdns_get_next_substring(buffer, capacity, string_table->offset[istr]); + if (!sub_string.length || (sub_string.length != first_length)) + continue; + if (memcmp(str, MDNS_POINTER_OFFSET(buffer, sub_string.offset), sub_string.length)) + continue; + + // Initial substring matches, now match all remaining substrings + offset += first_length + 1; + while (offset < total_length) { + size_t dot_pos = mdns_string_find(str, total_length, '.', offset); + if (dot_pos == MDNS_INVALID_POS) + dot_pos = total_length; + size_t current_length = dot_pos - offset; + + sub_string = + mdns_get_next_substring(buffer, capacity, sub_string.offset + sub_string.length); + if (!sub_string.length || (sub_string.length != current_length)) + break; + if (memcmp(str + offset, MDNS_POINTER_OFFSET(buffer, sub_string.offset), + sub_string.length)) + break; + + offset = dot_pos + 1; + } + + // Return reference offset if entire string matches + if (offset >= total_length) + return string_table->offset[istr]; + } + + return MDNS_INVALID_POS; +} + +static inline void +mdns_string_table_add(mdns_string_table_t* string_table, size_t offset) { + if (!string_table) + return; + + string_table->offset[string_table->next] = offset; + + size_t table_capacity = sizeof(string_table->offset) / sizeof(string_table->offset[0]); + if (++string_table->count > table_capacity) + string_table->count = table_capacity; + if (++string_table->next >= table_capacity) + string_table->next = 0; +} + +static inline size_t +mdns_string_find(const char* str, size_t length, char c, size_t offset) { + const void* found; + if (offset >= length) + return MDNS_INVALID_POS; + found = memchr(str + offset, c, length - offset); + if (found) + return (size_t)MDNS_POINTER_DIFF(found, str); + return MDNS_INVALID_POS; +} + +static inline void* +mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { + if (capacity < 2) + return 0; + return mdns_htons(data, 0xC000 | (uint16_t)ref_offset); +} + +static inline void* +mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, + mdns_string_table_t* string_table) { + size_t last_pos = 0; + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (name[length - 1] == '.') + --length; + while (last_pos < length) { + size_t pos = mdns_string_find(name, length, '.', last_pos); + size_t sub_length = ((pos != MDNS_INVALID_POS) ? pos : length) - last_pos; + size_t total_length = length - last_pos; + + size_t ref_offset = + mdns_string_table_find(string_table, buffer, capacity, + (char*)MDNS_POINTER_OFFSET(name, last_pos), sub_length, + total_length); + if (ref_offset != MDNS_INVALID_POS) + return mdns_string_make_ref(data, remain, ref_offset); + + if (remain <= (sub_length + 1)) + return 0; + + *(unsigned char*)data = (unsigned char)sub_length; + memcpy(MDNS_POINTER_OFFSET(data, 1), name + last_pos, sub_length); + mdns_string_table_add(string_table, MDNS_POINTER_DIFF(data, buffer)); + + data = MDNS_POINTER_OFFSET(data, sub_length + 1); + last_pos = ((pos != MDNS_INVALID_POS) ? pos + 1 : length); + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + } + + if (!remain) + return 0; + + *(unsigned char*)data = 0; + return MDNS_POINTER_OFFSET(data, 1); +} + +static inline size_t +mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const void* buffer, + size_t size, size_t* offset, mdns_entry_type_t type, uint16_t query_id, + size_t records, mdns_record_callback_fn callback, void* user_data) { + size_t parsed = 0; + for (size_t i = 0; i < records; ++i) { + size_t name_offset = *offset; + mdns_string_skip(buffer, size, offset); + if (((*offset) + 10) > size) + return parsed; + size_t name_length = (*offset) - name_offset; + const uint16_t* data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, *offset); + + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); + uint32_t ttl = mdns_ntohl(data); + data += 2; + uint16_t length = mdns_ntohs(data++); + + *offset += 10; + + if (length <= (size - (*offset))) { + ++parsed; + if (callback && + callback(sock, from, addrlen, type, query_id, rtype, rclass, ttl, buffer, size, + name_offset, name_length, *offset, length, user_data)) + break; + } + + *offset += length; + } + return parsed; +} + +static inline int +mdns_unicast_send(int sock, const void* address, size_t address_size, const void* buffer, + size_t size) { + if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address, + (socklen_t)address_size) < 0) + return -1; + return 0; +} + +static inline int +mdns_multicast_send(int sock, const void* buffer, size_t size) { + struct sockaddr_storage addr_storage; + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + struct sockaddr* saddr = (struct sockaddr*)&addr_storage; + socklen_t saddrlen = sizeof(struct sockaddr_storage); + if (getsockname(sock, saddr, &saddrlen)) + return -1; + if (saddr->sa_family == AF_INET6) { + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; +#ifdef __APPLE__ + addr6.sin6_len = sizeof(addr6); +#endif + addr6.sin6_addr.s6_addr[0] = 0xFF; + addr6.sin6_addr.s6_addr[1] = 0x02; + addr6.sin6_addr.s6_addr[15] = 0xFB; + addr6.sin6_port = htons((unsigned short)MDNS_PORT); + saddr = (struct sockaddr*)&addr6; + saddrlen = sizeof(addr6); + } else { + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; +#ifdef __APPLE__ + addr.sin_len = sizeof(addr); +#endif + addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); + addr.sin_port = htons((unsigned short)MDNS_PORT); + saddr = (struct sockaddr*)&addr; + saddrlen = sizeof(addr); + } + + if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0) + return -1; + return 0; +} + +static const uint8_t mdns_services_query[] = { + // Query ID + 0x00, 0x00, + // Flags + 0x00, 0x00, + // 1 question + 0x00, 0x01, + // No answer, authority or additional RRs + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // _services._dns-sd._udp.local. + 0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', 0x07, '_', 'd', 'n', 's', '-', 's', 'd', + 0x04, '_', 'u', 'd', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, + // PTR record + 0x00, MDNS_RECORDTYPE_PTR, + // QU (unicast response) and class IN + 0x80, MDNS_CLASS_IN}; + +static inline int +mdns_discovery_send(int sock) { + return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query)); +} + +static inline size_t +mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data) { + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); +#ifdef __APPLE__ + saddr->sa_len = sizeof(addr); +#endif + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + size_t records = 0; + const uint16_t* data = (uint16_t*)buffer; + + uint16_t query_id = mdns_ntohs(data++); + uint16_t flags = mdns_ntohs(data++); + uint16_t questions = mdns_ntohs(data++); + uint16_t answer_rrs = mdns_ntohs(data++); + uint16_t authority_rrs = mdns_ntohs(data++); + uint16_t additional_rrs = mdns_ntohs(data++); + + // According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case) + if (query_id || (flags != 0x8400)) + return 0; // Not a reply to our question + + // It seems some implementations do not fill the correct questions field, + // so ignore this check for now and only validate answer string + // if (questions != 1) + // return 0; + + int i; + for (i = 0; i < questions; ++i) { + size_t offset = MDNS_POINTER_DIFF(data, buffer); + size_t verify_offset = 12; + // Verify it's our question, _services._dns-sd._udp.local. + if (!mdns_string_equal(buffer, data_size, &offset, mdns_services_query, + sizeof(mdns_services_query), &verify_offset)) + return 0; + data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset); + + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); + + // Make sure we get a reply based on our PTR question for class IN + if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN)) + return 0; + } + + for (i = 0; i < answer_rrs; ++i) { + size_t offset = MDNS_POINTER_DIFF(data, buffer); + size_t verify_offset = 12; + // Verify it's an answer to our question, _services._dns-sd._udp.local. + size_t name_offset = offset; + int is_answer = mdns_string_equal(buffer, data_size, &offset, mdns_services_query, + sizeof(mdns_services_query), &verify_offset); + if (!is_answer && !mdns_string_skip(buffer, data_size, &offset)) + break; + size_t name_length = offset - name_offset; + if ((offset + 10) > data_size) + return records; + data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset); + + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); + uint32_t ttl = mdns_ntohl(data); + data += 2; + uint16_t length = mdns_ntohs(data++); + if (length > (data_size - offset)) + return 0; + + if (is_answer) { + ++records; + offset = MDNS_POINTER_DIFF(data, buffer); + if (callback && + callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, + buffer, data_size, name_offset, name_length, offset, length, user_data)) + return records; + } + data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(data, length); + } + + size_t total_records = records; + size_t offset = MDNS_POINTER_DIFF(data, buffer); + records = + mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); + total_records += records; + if (records != authority_rrs) + return total_records; + + records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, + user_data); + total_records += records; + if (records != additional_rrs) + return total_records; + + return total_records; +} + +static inline size_t +mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data) { + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); +#ifdef __APPLE__ + saddr->sa_len = sizeof(addr); +#endif + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + const uint16_t* data = (const uint16_t*)buffer; + + uint16_t query_id = mdns_ntohs(data++); + uint16_t flags = mdns_ntohs(data++); + uint16_t questions = mdns_ntohs(data++); + uint16_t answer_rrs = mdns_ntohs(data++); + uint16_t authority_rrs = mdns_ntohs(data++); + uint16_t additional_rrs = mdns_ntohs(data++); + + size_t records; + size_t total_records = 0; + for (int iquestion = 0; iquestion < questions; ++iquestion) { + size_t question_offset = MDNS_POINTER_DIFF(data, buffer); + size_t offset = question_offset; + size_t verify_offset = 12; + int dns_sd = 0; + if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, + sizeof(mdns_services_query), &verify_offset)) { + dns_sd = 1; + } else if (!mdns_string_skip(buffer, data_size, &offset)) { + break; + } + size_t length = offset - question_offset; + data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); + + uint16_t rtype = mdns_ntohs(data++); + uint16_t rclass = mdns_ntohs(data++); + uint16_t class_without_flushbit = rclass & ~MDNS_CACHE_FLUSH; + + // Make sure we get a question of class IN or ANY + if (!((class_without_flushbit == MDNS_CLASS_IN) || + (class_without_flushbit == MDNS_CLASS_ANY))) { + break; + } + + if (dns_sd && flags) + continue; + + ++total_records; + if (callback && callback(sock, saddr, addrlen, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, + rclass, 0, buffer, data_size, question_offset, length, + question_offset, length, user_data)) + return total_records; + } + + size_t offset = MDNS_POINTER_DIFF(data, buffer); + records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data); + total_records += records; + if (records != answer_rrs) + return total_records; + + records = + mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); + total_records += records; + if (records != authority_rrs) + return total_records; + + records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, + user_data); + + return total_records; +} + +static inline int +mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, + size_t capacity, uint16_t query_id) { + mdns_query_t query; + query.type = type; + query.name = name; + query.length = length; + return mdns_multiquery_send(sock, &query, 1, buffer, capacity, query_id); +} + +static inline int +mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, size_t capacity, + uint16_t query_id) { + if (!count || (capacity < (sizeof(struct mdns_header_t) + (6 * count)))) + return -1; + + // Ask for a unicast response since it's a one-shot query + uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE; + + struct sockaddr_storage addr_storage; + struct sockaddr* saddr = (struct sockaddr*)&addr_storage; + socklen_t saddrlen = sizeof(addr_storage); + if (getsockname(sock, saddr, &saddrlen) == 0) { + if ((saddr->sa_family == AF_INET) && + (ntohs(((struct sockaddr_in*)saddr)->sin_port) == MDNS_PORT)) + rclass &= ~MDNS_UNICAST_RESPONSE; + else if ((saddr->sa_family == AF_INET6) && + (ntohs(((struct sockaddr_in6*)saddr)->sin6_port) == MDNS_PORT)) + rclass &= ~MDNS_UNICAST_RESPONSE; + } + + struct mdns_header_t* header = (struct mdns_header_t*)buffer; + // Query ID + header->query_id = htons((unsigned short)query_id); + // Flags + header->flags = 0; + // Questions + header->questions = htons((unsigned short)count); + // No answer, authority or additional RRs + header->answer_rrs = 0; + header->authority_rrs = 0; + header->additional_rrs = 0; + // Fill in questions + void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); + for (size_t iq = 0; iq < count; ++iq) { + // Name string + data = mdns_string_make(buffer, capacity, data, query[iq].name, query[iq].length, 0); + if (!data) + return -1; + // Record type + data = mdns_htons(data, query[iq].type); + //! Optional unicast response based on local port, class IN + data = mdns_htons(data, rclass); + } + + size_t tosend = MDNS_POINTER_DIFF(data, buffer); + if (mdns_multicast_send(sock, buffer, (size_t)tosend)) + return -1; + return query_id; +} + +static inline size_t +mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data, int only_query_id) { + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); +#ifdef __APPLE__ + saddr->sa_len = sizeof(addr); +#endif + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + const uint16_t* data = (const uint16_t*)buffer; + + uint16_t query_id = mdns_ntohs(data++); + uint16_t flags = mdns_ntohs(data++); + uint16_t questions = mdns_ntohs(data++); + uint16_t answer_rrs = mdns_ntohs(data++); + uint16_t authority_rrs = mdns_ntohs(data++); + uint16_t additional_rrs = mdns_ntohs(data++); + (void)sizeof(flags); + + if ((only_query_id > 0) && (query_id != only_query_id)) + return 0; // Not a reply to the wanted one-shot query + + if (questions > 1) + return 0; + + // Skip questions part + int i; + for (i = 0; i < questions; ++i) { + size_t offset = MDNS_POINTER_DIFF(data, buffer); + if (!mdns_string_skip(buffer, data_size, &offset)) + return 0; + data = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); + // Record type and class not used, skip + // uint16_t rtype = mdns_ntohs(data++); + // uint16_t rclass = mdns_ntohs(data++); + data += 2; + } + + size_t records = 0; + size_t total_records = 0; + size_t offset = MDNS_POINTER_DIFF(data, buffer); + records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data); + total_records += records; + if (records != answer_rrs) + return total_records; + + records = + mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); + total_records += records; + if (records != authority_rrs) + return total_records; + + records = mdns_records_parse(sock, saddr, addrlen, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, + user_data); + total_records += records; + if (records != additional_rrs) + return total_records; + + return total_records; +} + +static inline void* +mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, + mdns_record_type_t record_type, const char* name, + size_t name_length, mdns_string_table_t* string_table) { + data = mdns_string_make(buffer, capacity, data, name, name_length, string_table); + if (!data) + return 0; + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (remain < 4) + return 0; + + data = mdns_htons(data, record_type); + data = mdns_htons(data, MDNS_UNICAST_RESPONSE | MDNS_CLASS_IN); + + return data; +} + +static inline void* +mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_record_t record, + mdns_string_table_t* string_table) { + data = mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table); + if (!data) + return 0; + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if (remain < 10) + return 0; + + data = mdns_htons(data, record.type); + data = mdns_htons(data, record.rclass); + data = mdns_htonl(data, record.ttl); + data = mdns_htons(data, 0); // Length, to be filled later + return data; +} + +static inline void* +mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t record, + mdns_string_table_t* string_table) { + // TXT records will be coalesced into one record later + if (!data || (record.type == MDNS_RECORDTYPE_TXT)) + return data; + + data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table); + if (!data) + return 0; + + // Pointer to length of record to be filled at end + void* record_length = MDNS_POINTER_OFFSET(data, -2); + void* record_data = data; + + size_t remain = capacity - MDNS_POINTER_DIFF(data, buffer); + switch (record.type) { + case MDNS_RECORDTYPE_PTR: + data = mdns_string_make(buffer, capacity, data, record.data.ptr.name.str, + record.data.ptr.name.length, string_table); + break; + + case MDNS_RECORDTYPE_SRV: + if (remain <= 6) + return 0; + data = mdns_htons(data, record.data.srv.priority); + data = mdns_htons(data, record.data.srv.weight); + data = mdns_htons(data, record.data.srv.port); + data = mdns_string_make(buffer, capacity, data, record.data.srv.name.str, + record.data.srv.name.length, string_table); + break; + + case MDNS_RECORDTYPE_A: + if (remain < 4) + return 0; + memcpy(data, &record.data.a.addr.sin_addr.s_addr, 4); + data = MDNS_POINTER_OFFSET(data, 4); + break; + + case MDNS_RECORDTYPE_AAAA: + if (remain < 16) + return 0; + memcpy(data, &record.data.aaaa.addr.sin6_addr, 16); // ipv6 address + data = MDNS_POINTER_OFFSET(data, 16); + break; + + default: + break; + } + + if (!data) + return 0; + + // Fill record length + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); + return data; +} + +static inline void +mdns_record_update_rclass_ttl(mdns_record_t* record, uint16_t rclass, uint32_t ttl) { + if (!record->rclass) + record->rclass = rclass; + if (!record->ttl || !ttl) + record->ttl = ttl; + record->rclass &= (uint16_t)(MDNS_CLASS_IN | MDNS_CACHE_FLUSH); + // Never flush PTR record + if (record->type == MDNS_RECORDTYPE_PTR) + record->rclass &= ~(uint16_t)MDNS_CACHE_FLUSH; +} + +static inline void* +mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, const mdns_record_t* records, + size_t record_count, uint16_t rclass, uint32_t ttl, + mdns_string_table_t* string_table) { + // Pointer to length of record to be filled at end + void* record_length = 0; + void* record_data = 0; + + size_t remain = 0; + for (size_t irec = 0; data && (irec < record_count); ++irec) { + if (records[irec].type != MDNS_RECORDTYPE_TXT) + continue; + + mdns_record_t record = records[irec]; + mdns_record_update_rclass_ttl(&record, rclass, ttl); + if (!record_data) { + data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table); + if (!data) + return data; + record_length = MDNS_POINTER_OFFSET(data, -2); + record_data = data; + } + + // TXT strings are unlikely to be shared, just make then raw. Also need one byte for + // termination, thus the <= check + size_t string_length = record.data.txt.key.length + record.data.txt.value.length + 1; + if (!data) + return 0; + remain = capacity - MDNS_POINTER_DIFF(data, buffer); + if ((remain <= string_length) || (string_length > 0x3FFF)) + return 0; + + unsigned char* strdata = (unsigned char*)data; + *strdata++ = (unsigned char)string_length; + memcpy(strdata, record.data.txt.key.str, record.data.txt.key.length); + strdata += record.data.txt.key.length; + *strdata++ = '='; + memcpy(strdata, record.data.txt.value.str, record.data.txt.value.length); + strdata += record.data.txt.value.length; + + data = strdata; + } + + // Fill record length + if (record_data) + mdns_htons(record_length, (uint16_t)MDNS_POINTER_DIFF(data, record_data)); + + return data; +} + +static inline uint16_t +mdns_answer_get_record_count(const mdns_record_t* records, size_t record_count) { + // TXT records will be coalesced into one record + uint16_t total_count = 0; + uint16_t txt_record = 0; + for (size_t irec = 0; irec < record_count; ++irec) { + if (records[irec].type == MDNS_RECORDTYPE_TXT) + txt_record = 1; + else + ++total_count; + } + return total_count + txt_record; +} + +static inline int +mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, + size_t capacity, uint16_t query_id, mdns_record_type_t record_type, + const char* name, size_t name_length, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) + return -1; + + // According to RFC 6762: + // The cache-flush bit MUST NOT be set in any resource records in a response message + // sent in legacy unicast responses to UDP ports other than 5353. + uint16_t rclass = MDNS_CLASS_IN; + uint32_t ttl = 10; + + // Basic answer structure + struct mdns_header_t* header = (struct mdns_header_t*)buffer; + header->query_id = htons(query_id); + header->flags = htons(0x8400); + header->questions = htons(1); + header->answer_rrs = htons(1); + header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); + header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); + + mdns_string_table_t string_table = {{0}, 0, 0}; + void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); + + // Fill in question + data = mdns_answer_add_question_unicast(buffer, capacity, data, record_type, name, name_length, + &string_table); + + // Fill in answer + answer.rclass = rclass; + answer.ttl = ttl; + data = mdns_answer_add_record(buffer, capacity, data, answer, &string_table); + + // Fill in authority records + for (size_t irec = 0; data && (irec < authority_count); ++irec) { + mdns_record_t record = authority[irec]; + record.rclass = rclass; + if (!record.ttl) + record.ttl = ttl; + data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); + } + data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, + rclass, ttl, &string_table); + + // Fill in additional records + for (size_t irec = 0; data && (irec < additional_count); ++irec) { + mdns_record_t record = additional[irec]; + record.rclass = rclass; + if (!record.ttl) + record.ttl = ttl; + data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); + } + data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, + rclass, ttl, &string_table); + if (!data) + return -1; + + size_t tosend = MDNS_POINTER_DIFF(data, buffer); + return mdns_unicast_send(sock, address, address_size, buffer, tosend); +} + +static inline int +mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count, + uint16_t rclass, uint32_t ttl) { + if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) + return -1; + + // Basic answer structure + struct mdns_header_t* header = (struct mdns_header_t*)buffer; + header->query_id = 0; + header->flags = htons(0x8400); + header->questions = 0; + header->answer_rrs = htons(1); + header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); + header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); + + mdns_string_table_t string_table = {{0}, 0, 0}; + void* data = MDNS_POINTER_OFFSET(buffer, sizeof(struct mdns_header_t)); + + // Fill in answer + mdns_record_t record = answer; + mdns_record_update_rclass_ttl(&record, rclass, ttl); + data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); + + // Fill in authority records + for (size_t irec = 0; data && (irec < authority_count); ++irec) { + record = authority[irec]; + mdns_record_update_rclass_ttl(&record, rclass, ttl); + data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); + } + data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, + rclass, ttl, &string_table); + + // Fill in additional records + for (size_t irec = 0; data && (irec < additional_count); ++irec) { + record = additional[irec]; + mdns_record_update_rclass_ttl(&record, rclass, ttl); + data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); + } + data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, + rclass, ttl, &string_table); + if (!data) + return -1; + + size_t tosend = MDNS_POINTER_DIFF(data, buffer); + return mdns_multicast_send(sock, buffer, tosend); +} + +static inline int +mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, + authority_count, additional, additional_count, + MDNS_CLASS_IN, 60); +} + +static inline int +mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, + authority_count, additional, additional_count, + MDNS_CLASS_IN | MDNS_CACHE_FLUSH, 60); +} + +static inline int +mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + // Goodbye should have ttl of 0 + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, + authority_count, additional, additional_count, + MDNS_CLASS_IN, 0); +} + +static inline mdns_string_t +mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity) { + // PTR record is just a string + if ((size >= offset + length) && (length >= 2)) + return mdns_string_extract(buffer, size, &offset, strbuffer, capacity); + mdns_string_t empty = {0, 0}; + return empty; +} + +static inline mdns_record_srv_t +mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity) { + mdns_record_srv_t srv; + memset(&srv, 0, sizeof(mdns_record_srv_t)); + // Read the service priority, weight, port number and the discovery name + // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): + // 2 bytes network-order unsigned priority + // 2 bytes network-order unsigned weight + // 2 bytes network-order unsigned port + // string: discovery (domain) name, minimum 2 bytes when compressed + if ((size >= offset + length) && (length >= 8)) { + const uint16_t* recorddata = (const uint16_t*)MDNS_POINTER_OFFSET_CONST(buffer, offset); + srv.priority = mdns_ntohs(recorddata++); + srv.weight = mdns_ntohs(recorddata++); + srv.port = mdns_ntohs(recorddata++); + offset += 6; + srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity); + } + return srv; +} + +static inline struct sockaddr_in* +mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in* addr) { + memset(addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; +#ifdef __APPLE__ + addr->sin_len = sizeof(struct sockaddr_in); +#endif + if ((size >= offset + length) && (length == 4)) + memcpy(&addr->sin_addr.s_addr, MDNS_POINTER_OFFSET(buffer, offset), 4); + return addr; +} + +static inline struct sockaddr_in6* +mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in6* addr) { + memset(addr, 0, sizeof(struct sockaddr_in6)); + addr->sin6_family = AF_INET6; +#ifdef __APPLE__ + addr->sin6_len = sizeof(struct sockaddr_in6); +#endif + if ((size >= offset + length) && (length == 16)) + memcpy(&addr->sin6_addr, MDNS_POINTER_OFFSET(buffer, offset), 16); + return addr; +} + +static inline size_t +mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, + mdns_record_txt_t* records, size_t capacity) { + size_t parsed = 0; + const char* strdata; + size_t end = offset + length; + + if (size < end) + end = size; + + while ((offset < end) && (parsed < capacity)) { + strdata = (const char*)MDNS_POINTER_OFFSET(buffer, offset); + size_t sublength = *(const unsigned char*)strdata; + + ++strdata; + offset += sublength + 1; + + size_t separator = 0; + for (size_t c = 0; c < sublength; ++c) { + // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] + if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) + break; + if (strdata[c] == '=') { + separator = c; + break; + } + } + + if (!separator) + continue; + + if (separator < sublength) { + records[parsed].key.str = strdata; + records[parsed].key.length = separator; + records[parsed].value.str = strdata + separator + 1; + records[parsed].value.length = sublength - (separator + 1); + } else { + records[parsed].key.str = strdata; + records[parsed].key.length = sublength; + } + + ++parsed; + } + + return parsed; +} + +#ifdef _WIN32 +#undef strncasecmp +#endif + +#ifdef __cplusplus +} +#endif diff --git a/sources b/sources new file mode 100644 index 0000000..eac1a48 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (hyperhdr-19.0.0.0.tar.gz) = 4f63dc701ae6df7f12f49ae6a02961f86eed0d0a659a7690c6775befd4edbd2321a735b1c240fc00b74f2149484f359e982139b304d9674673729e5dd1b9aa08
arch-excludes@lists.fedoraproject.org