2 * Copyright (c) 2020 Apple Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "dnssd_svcb.h"
19 #include <netinet/in.h>
23 dnssd_svcb_key_mandatory
= 0,
24 dnssd_svcb_key_alpn
= 1,
25 dnssd_svcb_key_no_default_alpn
= 2,
26 dnssd_svcb_key_port
= 3,
27 dnssd_svcb_key_ipv4_hint
= 4,
28 dnssd_svcb_key_ech_config
= 5,
29 dnssd_svcb_key_ipv6_hint
= 6,
30 dnssd_svcb_key_doh_uri
= 32768,
33 typedef bool (^_dnssd_svcb_access_value_block_t
)(const void *value
, size_t value_size
);
36 dnssd_svcb_get_priority(const uint8_t *buffer
, size_t buffer_size
)
38 if (buffer_size
< sizeof(uint16_t)) {
42 const uint16_t *priority_p
= (const uint16_t *)buffer
;
43 return (uint16_t)htons(*priority_p
);
46 #define DNSSD_MAX_DOMAIN_NAME 256
47 #define DNSSD_MAX_DOMAIN_LABEL 63
48 #define DNSSD_MAX_ESCAPED_DOMAIN_NAME 1009
51 _dnssd_svcb_get_domain_name_length(const uint8_t *buffer
, size_t buffer_size
, size_t *out_name_length
)
53 const uint8_t *limit
= buffer
+ buffer_size
;
54 const uint8_t *cursor
= buffer
;
55 while (cursor
!= NULL
&& cursor
< limit
) {
57 *out_name_length
= ((uint16_t)(cursor
- buffer
+ 1));
58 if (*out_name_length
> DNSSD_MAX_DOMAIN_NAME
) {
63 cursor
+= 1 + *cursor
;
69 _dnssd_svcb_convert_label_to_string(const uint8_t *source
, char *string_buffer
)
71 const uint8_t length
= *source
++; // Read length of this (non-null) label
72 const uint8_t *limit
= source
+ length
; // Work out where the label ends
73 if (length
> DNSSD_MAX_DOMAIN_LABEL
) {
76 while (source
< limit
) {
77 uint8_t character
= *source
++;
78 if (character
== '.' || character
== '\\') { // If character is a dot or the escape character
79 *string_buffer
++ = '\\'; // Output escape character
80 } else if (character
<= ' ') { // Output decimal escape sequence
81 *string_buffer
++ = '\\';
82 *string_buffer
++ = (char) ('0' + (character
/ 100));
83 *string_buffer
++ = (char) ('0' + (character
/ 10) % 10);
84 character
= (uint8_t)('0' + (character
) % 10);
86 *string_buffer
++ = (char)character
; // Copy the character
88 *string_buffer
= 0; // Null-terminate the string
89 return(string_buffer
); // and return
93 _dnssd_svcb_get_string_from_domain_name(const uint8_t *source
, char *string_buffer
)
95 const uint8_t *limit
= source
+ DNSSD_MAX_DOMAIN_NAME
;
98 *string_buffer
++ = '.'; // Special case: For root, just write a dot
102 if (source
+ 1 + *source
>= limit
) {
105 string_buffer
= _dnssd_svcb_convert_label_to_string(source
, string_buffer
);
106 if (string_buffer
== NULL
) {
109 source
+= 1 + *source
;
110 *string_buffer
++ = '.'; // Write the dot after the label
113 *string_buffer
++ = 0; // Null-terminate the string
114 return string_buffer
; // and return
118 dnssd_svcb_copy_domain(const uint8_t *buffer
, size_t buffer_size
)
120 if (buffer_size
< sizeof(uint16_t)) {
124 buffer
+= sizeof(uint16_t);
125 buffer_size
-= sizeof(uint16_t);
127 size_t domain_length
= 0;
128 if (!_dnssd_svcb_get_domain_name_length(buffer
, buffer_size
, &domain_length
)) {
132 char *name_str
= calloc(1, DNSSD_MAX_ESCAPED_DOMAIN_NAME
);
133 if (_dnssd_svcb_get_string_from_domain_name(buffer
, name_str
) == NULL
) {
141 _dnssd_svcb_extract_values(const uint8_t *buffer
, size_t buffer_size
,
142 dnssd_svcb_key_t match_key
, _dnssd_svcb_access_value_block_t value_block
)
144 if (buffer_size
< sizeof(uint16_t)) {
148 const uint16_t *priority_p
= (const uint16_t *)buffer
;
149 uint16_t priority
= (uint16_t)htons(*priority_p
);
151 // Alias form, no value
155 buffer
+= sizeof(uint16_t);
156 buffer_size
-= sizeof(uint16_t);
158 size_t domain_length
= 0;
159 if (!_dnssd_svcb_get_domain_name_length(buffer
, buffer_size
, &domain_length
)) {
163 buffer
+= domain_length
;
164 buffer_size
-= domain_length
;
166 while (buffer
!= NULL
&& buffer_size
>= (sizeof(uint16_t) + sizeof(uint16_t))) {
167 const uint16_t *param_key_p
= (const uint16_t *)buffer
;
168 uint16_t param_key
= (uint16_t)htons(*param_key_p
);
170 buffer
+= sizeof(uint16_t);
171 buffer_size
-= sizeof(uint16_t);
173 const uint16_t *param_value_length_p
= (const uint16_t *)buffer
;
174 uint16_t param_value_length
= (uint16_t)htons(*param_value_length_p
);
176 buffer
+= sizeof(uint16_t);
177 buffer_size
-= sizeof(uint16_t);
179 if (param_value_length
> buffer_size
) {
183 if (match_key
== param_key
) {
184 bool continue_looping
= value_block(buffer
, param_value_length
);
185 if (!continue_looping
) {
190 buffer
+= param_value_length
;
191 buffer_size
-= param_value_length
;
198 dnssd_svcb_is_valid(const uint8_t *buffer
, size_t buffer_size
)
200 if (buffer_size
< sizeof(uint16_t)) {
204 uint16_t priority
= dnssd_svcb_get_priority(buffer
, buffer_size
);
206 // Alias forms don't need further validation
210 __block
bool invalid_mandatory_value
= false;
211 (void)_dnssd_svcb_extract_values(buffer
, buffer_size
, dnssd_svcb_key_mandatory
, ^bool(const void *value
, size_t value_size
) {
212 if (value
!= NULL
&& value_size
> 0) {
213 if ((value_size
% sizeof(uint16_t)) != 0) {
214 // Value must be a list of keys, as 16-bit integers
215 invalid_mandatory_value
= true;
217 const uint16_t mandatory_key_count
= (uint16_t)(value_size
/ sizeof(uint16_t));
218 for (uint16_t i
= 0; i
< mandatory_key_count
&& !invalid_mandatory_value
; i
++) {
219 const uint16_t *param_key_p
= ((const uint16_t *)value
) + i
;
220 uint16_t param_key
= (uint16_t)htons(*param_key_p
);
222 case dnssd_svcb_key_mandatory
:
223 // Mandatory key cannot be listed
224 invalid_mandatory_value
= true;
226 case dnssd_svcb_key_alpn
:
227 case dnssd_svcb_key_no_default_alpn
:
228 case dnssd_svcb_key_port
:
229 case dnssd_svcb_key_ipv4_hint
:
230 case dnssd_svcb_key_ech_config
:
231 case dnssd_svcb_key_ipv6_hint
:
232 case dnssd_svcb_key_doh_uri
:
233 // Known keys are fine
236 // Unknown mandatory key means we should ignore the record
237 invalid_mandatory_value
= true;
245 if (invalid_mandatory_value
) {
253 dnssd_svcb_get_port(const uint8_t *buffer
, size_t buffer_size
)
255 __block
uint16_t port
= false;
256 (void)_dnssd_svcb_extract_values(buffer
, buffer_size
, dnssd_svcb_key_port
, ^bool(const void *value
, size_t value_size
) {
257 if (value
!= NULL
&& value_size
== sizeof(uint16_t)) {
258 port
= (uint16_t)htons(*(const uint16_t *)value
);
266 dnssd_svcb_copy_doh_uri(const uint8_t *buffer
, size_t buffer_size
)
268 __block
char *doh_uri
= NULL
;
269 (void)_dnssd_svcb_extract_values(buffer
, buffer_size
, dnssd_svcb_key_doh_uri
, ^bool(const void *value
, size_t value_size
) {
270 if (value
!= NULL
&& value_size
> 0) {
271 asprintf(&doh_uri
, "%.*s", (int)value_size
, value
);
279 dnssd_svcb_copy_ech_config(const uint8_t *buffer
, size_t buffer_size
, size_t *out_length
)
281 __block
uint8_t *ech_config
= NULL
;
282 (void)_dnssd_svcb_extract_values(buffer
, buffer_size
, dnssd_svcb_key_ech_config
, ^bool(const void *value
, size_t value_size
) {
283 if (value
!= NULL
&& value_size
> 0) {
284 ech_config
= calloc(1, value_size
);
285 *out_length
= value_size
;
286 memcpy(ech_config
, value
, value_size
);
294 dnssd_svcb_access_alpn_values(const uint8_t *buffer
, size_t buffer_size
,
295 DNSSD_NOESCAPE _dnssd_svcb_access_alpn_t block
)
297 (void)_dnssd_svcb_extract_values(buffer
, buffer_size
, dnssd_svcb_key_alpn
, ^bool(const void *value
, size_t value_size
) {
299 size_t value_read
= 0;
300 while (value_size
> 0 && value_read
< value_size
) {
301 char alpn_value
[UINT8_MAX
] = "";
303 uint8_t alpn_length
= *(const uint8_t *)value
;
306 if (value_read
+ alpn_length
> value_size
) {
310 memcpy(alpn_value
, ((const uint8_t *)value
) + value_read
, alpn_length
);
311 if (!block((const char *)alpn_value
)) {
314 value_read
+= alpn_length
;
322 dnssd_svcb_access_address_hints(const uint8_t *buffer
, size_t buffer_size
, DNSSD_NOESCAPE _dnssd_svcb_access_address_t block
)
324 __block
bool continue_enumerating
= true;
325 (void)_dnssd_svcb_extract_values(buffer
, buffer_size
, dnssd_svcb_key_ipv4_hint
, ^bool(const void *value
, size_t value_size
) {
326 if (value
!= NULL
&& (value_size
% sizeof(struct in_addr
)) == 0) {
327 size_t value_read
= 0;
328 while (value_read
< value_size
) {
329 struct sockaddr_in v4addr
;
330 memset(&v4addr
, 0, sizeof(v4addr
));
331 v4addr
.sin_family
= AF_INET
;
332 v4addr
.sin_len
= sizeof(v4addr
);
333 memcpy(&v4addr
.sin_addr
, ((const uint8_t *)value
) + value_read
, sizeof(struct in_addr
));
334 continue_enumerating
= block((const struct sockaddr
*)&v4addr
);
335 if (!continue_enumerating
) {
338 value_read
+= sizeof(struct in_addr
);
343 if (!continue_enumerating
) {
346 (void)_dnssd_svcb_extract_values(buffer
, buffer_size
, dnssd_svcb_key_ipv6_hint
, ^bool(const void *value
, size_t value_size
) {
348 if (value
!= NULL
&& (value_size
% sizeof(struct in6_addr
)) == 0) {
349 size_t value_read
= 0;
350 while (value_read
< value_size
) {
351 struct sockaddr_in6 v6addr
;
352 memset(&v6addr
, 0, sizeof(v6addr
));
353 v6addr
.sin6_family
= AF_INET6
;
354 v6addr
.sin6_len
= sizeof(v6addr
);
355 memcpy(&v6addr
.sin6_addr
, ((const uint8_t *)value
) + value_read
, sizeof(struct in6_addr
));
356 continue_enumerating
= block((const struct sockaddr
*)&v6addr
);
357 if (!continue_enumerating
) {
360 value_read
+= sizeof(struct in6_addr
);